1 |
|
---|
2 | /*
|
---|
3 | *@@sourcefile w_xsystray.c:
|
---|
4 | * Extended system tray widget for XCenter/eCenter.
|
---|
5 | *
|
---|
6 | * Implementation.
|
---|
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 | #if defined(__IBMC__) || defined(__IBMCPP__)
|
---|
22 | #pragma strings(readonly)
|
---|
23 | #endif
|
---|
24 |
|
---|
25 | /*
|
---|
26 | * Suggested #include order:
|
---|
27 | * 1) os2.h
|
---|
28 | * 2) C library headers
|
---|
29 | * 3) setup.h (code generation and debugging options)
|
---|
30 | * 4) headers in helpers\
|
---|
31 | * 5) at least one SOM implementation header (*.ih)
|
---|
32 | * 6) dlgids.h, headers in shared\ (as needed)
|
---|
33 | * 7) headers in implementation dirs (e.g. filesys\, as needed)
|
---|
34 | * 8) #pragma hdrstop and then more SOM headers which crash with precompiled headers
|
---|
35 | */
|
---|
36 |
|
---|
37 | #if defined(__GNUC__) && defined(__EMX__)
|
---|
38 | #define OS2EMX_PLAIN_CHAR
|
---|
39 | #endif
|
---|
40 |
|
---|
41 | #define INCL_BASE
|
---|
42 | #define INCL_PM
|
---|
43 | #include <os2.h>
|
---|
44 |
|
---|
45 | // C library headers
|
---|
46 | #include <stdio.h>
|
---|
47 | #include <string.h>
|
---|
48 | #include <stdlib.h>
|
---|
49 |
|
---|
50 | // generic headers
|
---|
51 | // If this file were part of the XWorkplace sources, we'd now include
|
---|
52 | // the generic "setup.h" file, which has common set up code for every
|
---|
53 | // single XWorkplace code file. But it's not, so we won't include that.
|
---|
54 | // #include "setup.h" // code generation and debugging options
|
---|
55 |
|
---|
56 | // headers in /helpers
|
---|
57 | // This would be the place to include headers from the "XWorkplace helpers".
|
---|
58 | // But since we do a minimal sample here, we can't include those helpful
|
---|
59 | // routines... again, see the src\widgets in the XWorkplace source code
|
---|
60 | // for how these functions can be imported from XFLDR.DLL to avoid duplicate
|
---|
61 | // code.
|
---|
62 | // #include "helpers\dosh.h" // Control Program helper routines
|
---|
63 | // #include "helpers\gpih.h" // GPI helper routines
|
---|
64 | // #include "helpers\prfh.h" // INI file helper routines;
|
---|
65 | // this include is required for some
|
---|
66 | // of the structures in shared\center.h
|
---|
67 | // #include "helpers\winh.h" // PM helper routines
|
---|
68 | // #include "helpers\xstring.h" // extended string helpers
|
---|
69 |
|
---|
70 | // XWorkplace implementation headers
|
---|
71 | // If this file were part of the XCenter sources, we'd now include
|
---|
72 | // "center.h" from the "include\shared" directory. Since we're not
|
---|
73 | // part of the XCenter sources here, we include that file from the
|
---|
74 | // "toolkit" directory in the binary release. That file is identical
|
---|
75 | // to "include\shared\center.h" in the XWorkplace sources.
|
---|
76 | #include "shared\center.h" // public XCenter interfaces
|
---|
77 |
|
---|
78 | //#define ENABLE_LOG_TO "c:\\xsystray_plugin.dbg"
|
---|
79 |
|
---|
80 | #include "w_xsystray.h"
|
---|
81 |
|
---|
82 | #if defined(__IBMC__) || defined(__IBMCPP__)
|
---|
83 | #pragma hdrstop // VAC++ keeps crashing otherwise
|
---|
84 | #endif
|
---|
85 |
|
---|
86 | // copy paste from helpers\comctl.h
|
---|
87 | // @todo not necessary when we become part of XWorkplace
|
---|
88 |
|
---|
89 | #define TTN_NEEDTEXT 1000
|
---|
90 | #define TTN_SHOW 1001
|
---|
91 | #define TTN_POP 1002
|
---|
92 |
|
---|
93 | #define TTFMT_PSZ 0x01
|
---|
94 | #define TTFMT_STRINGRES 0x02
|
---|
95 |
|
---|
96 | typedef struct _TOOLTIPTEXT
|
---|
97 | {
|
---|
98 | HWND hwndTooltip;
|
---|
99 | HWND hwndTool;
|
---|
100 | ULONG ulFormat;
|
---|
101 | PSZ pszText;
|
---|
102 | HMODULE hmod;
|
---|
103 | ULONG idResource;
|
---|
104 | } TOOLTIPTEXT, *PTOOLTIPTEXT;
|
---|
105 |
|
---|
106 | #define TTM_FIRST (WM_USER + 1000)
|
---|
107 | #define TTM_UPDATETIPTEXT (TTM_FIRST + 9)
|
---|
108 | #define TTM_SHOWTOOLTIPNOW (TTM_FIRST + 17)
|
---|
109 |
|
---|
110 | /* ******************************************************************
|
---|
111 | *
|
---|
112 | * Private definitions
|
---|
113 | *
|
---|
114 | ********************************************************************/
|
---|
115 |
|
---|
116 | /*
|
---|
117 | *@@ ICONDATA:
|
---|
118 | * Per-icon data.
|
---|
119 | */
|
---|
120 |
|
---|
121 | typedef struct
|
---|
122 | {
|
---|
123 | HWND hwnd;
|
---|
124 | // associated window
|
---|
125 | USHORT usId;
|
---|
126 | // icon ID
|
---|
127 | HPOINTER hIcon;
|
---|
128 | // icon handle
|
---|
129 | ULONG ulMsgId;
|
---|
130 | // message ID for notifications
|
---|
131 | PSZ pszToolTip;
|
---|
132 | // icon tooltip (NULL if none)
|
---|
133 | BOOL bIsToolTipShowing;
|
---|
134 | // whether the tooltip is currently shown
|
---|
135 | BOOL bMemoryPoolGiven;
|
---|
136 | // TRUE if SYSTRAYDATA::pvMemoryPool is already given to
|
---|
137 | // the process hwnd belongs to
|
---|
138 |
|
---|
139 | } ICONDATA, *PICONDATA;
|
---|
140 |
|
---|
141 | /*
|
---|
142 | *@@ SYSTRAYDATA:
|
---|
143 | * Global system tray data.
|
---|
144 | */
|
---|
145 |
|
---|
146 | typedef struct
|
---|
147 | {
|
---|
148 | HWND hwndServer;
|
---|
149 | // systtem tray server window handle
|
---|
150 | LONG lIconWidth;
|
---|
151 | // system icon width in px
|
---|
152 | LONG lIconHeight;
|
---|
153 | // system icon height in px
|
---|
154 | LONG lIconPad;
|
---|
155 | // padding around each icon in px
|
---|
156 | PICONDATA pIcons;
|
---|
157 | // array of icons currently shown in the system tray
|
---|
158 | size_t cIcons;
|
---|
159 | // number of icons in the pIcons array
|
---|
160 | size_t cIconsMax;
|
---|
161 | // maximum number of icons pIcons can fit
|
---|
162 | CHAR szToolTip[sizeof(((PSYSTRAYCTLDATA)0)->u.icon.szToolTip)];
|
---|
163 | // "static" buffer for the tooltip (XCenter requirement)
|
---|
164 | PVOID pvMemoryPool;
|
---|
165 | // memory pool for NOTIFYDATA structures
|
---|
166 |
|
---|
167 | } SYSTRAYDATA, *PSYSTRAYDATA;
|
---|
168 |
|
---|
169 | #define ICONARRAY_GROW 4
|
---|
170 | // number of element the icon array is increased by when there is no
|
---|
171 | // space for the newly added icon
|
---|
172 |
|
---|
173 | #define SERVER_MEMORYPOOL_SIZE 65536
|
---|
174 | // taking NOTIFYDATA size into account (<=32 B), this is enough for at
|
---|
175 | // least 2048 simultaneous notification messages, which (even taking
|
---|
176 | // slowly responsing clients into account) sounds sane since in most
|
---|
177 | // cases the structure is freed once it reaches the target event queue
|
---|
178 | // and before a message created as a copy of it is sent to the target
|
---|
179 | // window procedure
|
---|
180 |
|
---|
181 | #define TID_CHECKALIVE 1
|
---|
182 | // timer that checks if windows associated with icons are still alive
|
---|
183 | #define TID_CHECKALIVE_TIMEOUT 2000 // ms
|
---|
184 | // how often to perform alive checks
|
---|
185 |
|
---|
186 | static ULONG WM_XST_CREATED = 0;
|
---|
187 | // identity of the WM_XST_CREATED message taken from the atom table
|
---|
188 | static ULONG WM_XST_NOTIFY = 0;
|
---|
189 | // identity of the WM_XST_NOTIFY message taken from the atom table
|
---|
190 |
|
---|
191 | static ULONG QWL_USER_SERVER_DATA = 0;
|
---|
192 | // offset to the PXCENTERWIDGET pointer in the widget data array
|
---|
193 |
|
---|
194 | static
|
---|
195 | VOID WgtXSysTrayUpdateAfterIconAddRemove(PXCENTERWIDGET pWidget);
|
---|
196 |
|
---|
197 | /* ******************************************************************
|
---|
198 | *
|
---|
199 | * XCenter widget class definition
|
---|
200 | *
|
---|
201 | ********************************************************************/
|
---|
202 |
|
---|
203 | /*
|
---|
204 | * This contains the name of the PM window class and
|
---|
205 | * the XCENTERWIDGETCLASS definition(s) for the widget
|
---|
206 | * class(es) in this DLL.
|
---|
207 | *
|
---|
208 | * The address of this structure (or an array of these
|
---|
209 | * structures, if there were several widget classes in
|
---|
210 | * this plugin) is returned by the "init" export
|
---|
211 | * (WgtInitModule).
|
---|
212 | */
|
---|
213 |
|
---|
214 | static const XCENTERWIDGETCLASS G_WidgetClasses[] =
|
---|
215 | {
|
---|
216 | {
|
---|
217 | WNDCLASS_WIDGET_XSYSTRAY, // PM window class name
|
---|
218 | 0, // additional flag, not used here
|
---|
219 | INTCLASS_WIDGET_XSYSTRAY, // internal widget class name
|
---|
220 | HUMANSTR_WIDGET_XSYSTRAY, // widget class name displayed to user
|
---|
221 | WGTF_UNIQUEGLOBAL | // widget class flags
|
---|
222 | WGTF_TOOLTIP,
|
---|
223 | NULL // no settings dialog
|
---|
224 | }
|
---|
225 | };
|
---|
226 |
|
---|
227 | /* ******************************************************************
|
---|
228 | *
|
---|
229 | * Function imports from XFLDR.DLL
|
---|
230 | *
|
---|
231 | ********************************************************************/
|
---|
232 |
|
---|
233 | /*
|
---|
234 | * To reduce the size of the widget DLL, it can
|
---|
235 | * be compiled with the VAC subsystem libraries.
|
---|
236 | * In addition, instead of linking frequently
|
---|
237 | * used helpers against the DLL again, you can
|
---|
238 | * import them from XFLDR.DLL, whose module handle
|
---|
239 | * is given to you in the INITMODULE export.
|
---|
240 | *
|
---|
241 | * Note that importing functions from XFLDR.DLL
|
---|
242 | * is _not_ a requirement. We can't do this in
|
---|
243 | * this minimal sample anyway without having access
|
---|
244 | * to the full XWorkplace source code.
|
---|
245 | *
|
---|
246 | * If you want to know how you can import the useful
|
---|
247 | * functions from XFLDR.DLL to use them in your widget
|
---|
248 | * plugin, again, see src\widgets in the XWorkplace sources.
|
---|
249 | * The actual imports would then be made by WgtInitModule.
|
---|
250 | */
|
---|
251 |
|
---|
252 | // @todo the function declarations should become not necessary when we move into
|
---|
253 | // XWorkplace. Let's hope the prototypes will not change until then. Let's also
|
---|
254 | // pray that XFLDRxxx.DLL has the _Optlink calling convention for exports and
|
---|
255 | // not the default __cdecl (which happens if it builds with GCC according to the
|
---|
256 | // XWPENTRY definition in the current xwphelpers sources)
|
---|
257 |
|
---|
258 | #define XWPENTRY _Optlink
|
---|
259 |
|
---|
260 | typedef VOID XWPENTRY GPIHDRAW3DFRAME(HPS hps,
|
---|
261 | PRECTL prcl,
|
---|
262 | USHORT usWidth,
|
---|
263 | LONG lColorLeft,
|
---|
264 | LONG lColorRight);
|
---|
265 | typedef GPIHDRAW3DFRAME *PGPIHDRAW3DFRAME;
|
---|
266 | PGPIHDRAW3DFRAME pgpihDraw3DFrame = NULL;
|
---|
267 |
|
---|
268 | typedef struct _RESOLVEFUNCTION
|
---|
269 | {
|
---|
270 | const char *pcszFunctionName;
|
---|
271 | PFN *ppFuncAddress;
|
---|
272 | } RESOLVEFUNCTION, *PRESOLVEFUNCTION;
|
---|
273 | RESOLVEFUNCTION G_aImports[] =
|
---|
274 | {
|
---|
275 | { "gpihDraw3DFrame", (PFN*)&pgpihDraw3DFrame },
|
---|
276 | };
|
---|
277 |
|
---|
278 | /* ******************************************************************
|
---|
279 | *
|
---|
280 | * Private widget instance data
|
---|
281 | *
|
---|
282 | ********************************************************************/
|
---|
283 |
|
---|
284 | // None presently. The samples in src\widgets in the XWorkplace
|
---|
285 | // sources cleanly separate setup string data from other widget
|
---|
286 | // instance data to allow for easier manipulation with settings
|
---|
287 | // dialogs. We have skipped this for the minimal sample.
|
---|
288 |
|
---|
289 | /* ******************************************************************
|
---|
290 | *
|
---|
291 | * Widget setup management
|
---|
292 | *
|
---|
293 | ********************************************************************/
|
---|
294 |
|
---|
295 | // None presently. See above.
|
---|
296 |
|
---|
297 | /* ******************************************************************
|
---|
298 | *
|
---|
299 | * Widget settings dialog
|
---|
300 | *
|
---|
301 | ********************************************************************/
|
---|
302 |
|
---|
303 | // None currently. To see how a setup dialog can be done,
|
---|
304 | // see the "window list" widget in the XWorkplace sources
|
---|
305 | // (src\widgets\w_winlist.c).
|
---|
306 |
|
---|
307 | /* ******************************************************************
|
---|
308 | *
|
---|
309 | * Callbacks stored in XCENTERWIDGETCLASS
|
---|
310 | *
|
---|
311 | ********************************************************************/
|
---|
312 |
|
---|
313 | // If you implement a settings dialog, you must write a
|
---|
314 | // "show settings dlg" function and store its function pointer
|
---|
315 | // in XCENTERWIDGETCLASS.
|
---|
316 |
|
---|
317 | /* ******************************************************************
|
---|
318 | *
|
---|
319 | * Helper methods
|
---|
320 | *
|
---|
321 | ********************************************************************/
|
---|
322 |
|
---|
323 | /*
|
---|
324 | *@@ FreeIconData:
|
---|
325 | * Frees all members of the ICONDATA structure and resets them to 0.
|
---|
326 | */
|
---|
327 |
|
---|
328 | static
|
---|
329 | VOID FreeIconData(PICONDATA pData)
|
---|
330 | {
|
---|
331 | pData->hwnd = NULLHANDLE;
|
---|
332 | pData->usId = 0;
|
---|
333 | if (pData->hIcon != NULLHANDLE)
|
---|
334 | {
|
---|
335 | WinDestroyPointer(pData->hIcon);
|
---|
336 | pData->hIcon = NULLHANDLE;
|
---|
337 |
|
---|
338 | }
|
---|
339 | pData->ulMsgId = 0;
|
---|
340 | if (pData->pszToolTip)
|
---|
341 | {
|
---|
342 | free(pData->pszToolTip);
|
---|
343 | pData->pszToolTip = NULL;
|
---|
344 | }
|
---|
345 | }
|
---|
346 |
|
---|
347 | /*
|
---|
348 | *@@ FindIconData:
|
---|
349 | * Searches for the icon with the given identity. Returns NULL if not
|
---|
350 | * found.
|
---|
351 | */
|
---|
352 |
|
---|
353 | static
|
---|
354 | PICONDATA FindIconData(PSYSTRAYDATA pSysTrayData,
|
---|
355 | HWND hwnd, // in: associated window handle
|
---|
356 | USHORT usId, // in: icon ID
|
---|
357 | size_t *pIdx) // out: index of the icon in the icon array
|
---|
358 | // (optional, may be NULL)
|
---|
359 | {
|
---|
360 | size_t i;
|
---|
361 | for (i = 0; i < pSysTrayData->cIcons; ++i)
|
---|
362 | {
|
---|
363 | if (pSysTrayData->pIcons[i].hwnd == hwnd &&
|
---|
364 | pSysTrayData->pIcons[i].usId == usId)
|
---|
365 | {
|
---|
366 | if (pIdx)
|
---|
367 | *pIdx = i;
|
---|
368 | return &pSysTrayData->pIcons[i];
|
---|
369 | }
|
---|
370 | }
|
---|
371 |
|
---|
372 | if (pIdx)
|
---|
373 | *pIdx = i;
|
---|
374 | return NULL;
|
---|
375 | }
|
---|
376 |
|
---|
377 | /*
|
---|
378 | *@@ FindIconDataAtPt:
|
---|
379 | * Searches for the icon under the given point.
|
---|
380 | * Returns NULL if no icon found (e.g. the pad space).
|
---|
381 | *
|
---|
382 | * Refer to WgtPaint() for system tray geometry description.
|
---|
383 | */
|
---|
384 |
|
---|
385 | static
|
---|
386 | PICONDATA FindIconDataAtPt(PXCENTERWIDGET pWidget,
|
---|
387 | PPOINTL pptl, // point coordinates (relative to systray)
|
---|
388 | size_t *pIdx) // out: index of the icon in the icon array
|
---|
389 | // (optional, may be NULL)
|
---|
390 | {
|
---|
391 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
392 |
|
---|
393 | SWP swp;
|
---|
394 | RECTL rcl;
|
---|
395 | BOOL bLeftToRight;
|
---|
396 | LONG y, lIconStep;
|
---|
397 | size_t i;
|
---|
398 |
|
---|
399 | // start with invalid index index
|
---|
400 | if (pIdx)
|
---|
401 | *pIdx = pSysTrayData->cIcons;
|
---|
402 |
|
---|
403 | WinQueryWindowPos(pWidget->hwndWidget, &swp);
|
---|
404 | WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
|
---|
405 |
|
---|
406 | y = (swp.cy - pSysTrayData->lIconHeight) / 2;
|
---|
407 | if (pptl->y < y || pptl->y >= y + pSysTrayData->lIconHeight)
|
---|
408 | return NULL; // hit pad space
|
---|
409 |
|
---|
410 | // detect the direction
|
---|
411 | bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
|
---|
412 |
|
---|
413 | lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
|
---|
414 |
|
---|
415 | // which icon is that?
|
---|
416 | if (bLeftToRight)
|
---|
417 | {
|
---|
418 | i = pptl->x / lIconStep;
|
---|
419 | if (pptl->x % lIconStep < pSysTrayData->lIconPad)
|
---|
420 | return NULL; // hit pad space
|
---|
421 | }
|
---|
422 | else
|
---|
423 | {
|
---|
424 | i = (swp.cx - pptl->x - 1) / lIconStep;
|
---|
425 | if ((swp.cx - pptl->x - 1) % lIconStep < pSysTrayData->lIconPad)
|
---|
426 | return NULL; // hit pad space
|
---|
427 | }
|
---|
428 | if (i >= pSysTrayData->cIcons)
|
---|
429 | return NULL; // hit pad space
|
---|
430 |
|
---|
431 | if (pIdx)
|
---|
432 | *pIdx = i;
|
---|
433 | return &pSysTrayData->pIcons[i];
|
---|
434 | }
|
---|
435 |
|
---|
436 | static
|
---|
437 | VOID FreeSysTrayData(PSYSTRAYDATA pSysTrayData)
|
---|
438 | {
|
---|
439 | // destroy the server
|
---|
440 | if (pSysTrayData->hwndServer != NULLHANDLE)
|
---|
441 | {
|
---|
442 | WinDestroyWindow(pSysTrayData->hwndServer);
|
---|
443 | pSysTrayData->hwndServer = NULLHANDLE;
|
---|
444 | }
|
---|
445 |
|
---|
446 | // free all system tray data
|
---|
447 | if (pSysTrayData->pvMemoryPool)
|
---|
448 | {
|
---|
449 | DosFreeMem(pSysTrayData->pvMemoryPool);
|
---|
450 | }
|
---|
451 | if (pSysTrayData->pIcons)
|
---|
452 | {
|
---|
453 | size_t i;
|
---|
454 | for (i = 0; i < pSysTrayData->cIcons; ++i)
|
---|
455 | FreeIconData(&pSysTrayData->pIcons[i]);
|
---|
456 | pSysTrayData->cIcons = 0;
|
---|
457 | free(pSysTrayData->pIcons);
|
---|
458 | pSysTrayData->pIcons = NULL;
|
---|
459 | }
|
---|
460 |
|
---|
461 | free(pSysTrayData);
|
---|
462 | }
|
---|
463 |
|
---|
464 | /*
|
---|
465 | *@@ AllocNotifyDataPtr:
|
---|
466 | * Allocates a SYSTRAYCTLDATA struct in the pool of shared memory on belalf
|
---|
467 | * of the client window identified by pIconData->hwnd.
|
---|
468 | *
|
---|
469 | * If there is no free space in the pool, it returns NULL. On success,
|
---|
470 | * fills in msg and mp1 fields of the returned structure using the
|
---|
471 | * information provided in pIconData.
|
---|
472 | *
|
---|
473 | * Note that the allocated structure is supposed to be freed using
|
---|
474 | * FreeNotifyDataPtr by the client-side API implementation in another
|
---|
475 | * process.
|
---|
476 | */
|
---|
477 | static
|
---|
478 | PNOTIFYDATA AllocNotifyDataPtr(PVOID pvMemoryPool, // in: memory pool base address
|
---|
479 | PICONDATA pIconData) // in: icon data
|
---|
480 | {
|
---|
481 | // NOTE: we cannot use DosSubAllocMem() and friends since we want to be able
|
---|
482 | // to free blocks allocated to clients which death we detect but we cannot
|
---|
483 | // be sure about the layout of memory DosSub API uses. Therefore, we provide
|
---|
484 | // our own sub-allocation scheme which is rather simple because we need to
|
---|
485 | // allocate equally sized blocks (each is NOTIFYDATA struct). The memory
|
---|
486 | // pool is laid out as follows: MEMPOOLHDR followed by a number of
|
---|
487 | // MEMPOOLBLK.
|
---|
488 |
|
---|
489 | APIRET arc;
|
---|
490 | PID pid;
|
---|
491 | TID tid;
|
---|
492 | PMEMPOOLHDR pHdr;
|
---|
493 | ULONG ulMax, ulNeedsCommit, ulNext, ulCurr;
|
---|
494 | PNOTIFYDATA pData;
|
---|
495 |
|
---|
496 | LOGF(("pvMemoryPool %p\n", pvMemoryPool));
|
---|
497 | LOGF(("hwnd %lx\n", pIconData->hwnd));
|
---|
498 |
|
---|
499 | if (!pIconData->bMemoryPoolGiven)
|
---|
500 | {
|
---|
501 | LOGF(("Giving memory pool to %lx\n", pIconData->hwnd));
|
---|
502 |
|
---|
503 | arc = ERROR_INVALID_HANDLE;
|
---|
504 | if (WinQueryWindowProcess(pIconData->hwnd, &pid, &tid))
|
---|
505 | arc = DosGiveSharedMem(pvMemoryPool, pid, PAG_READ | PAG_WRITE);
|
---|
506 | if (arc != NO_ERROR)
|
---|
507 | return NULL;
|
---|
508 |
|
---|
509 | pIconData->bMemoryPoolGiven = TRUE;
|
---|
510 | }
|
---|
511 |
|
---|
512 | pHdr = (PMEMPOOLHDR)pvMemoryPool;
|
---|
513 |
|
---|
514 | // maximum address that is still enough for a block
|
---|
515 | ulMax = pHdr->ulBeyond - sizeof(MEMPOOLBLK);
|
---|
516 |
|
---|
517 | ulNeedsCommit = pHdr->ulNeedsCommit;
|
---|
518 | ulNext = pHdr->ulNext;
|
---|
519 | ulCurr = ulNext;
|
---|
520 |
|
---|
521 | LOGF(("ulNeedsCommit %p\n", ulNeedsCommit));
|
---|
522 | LOGF(("ulNext %p\n", ulNext));
|
---|
523 |
|
---|
524 | do
|
---|
525 | {
|
---|
526 | if (ulCurr >= ulNeedsCommit)
|
---|
527 | {
|
---|
528 | // commit more memory; it's OK two or more threads will do the same
|
---|
529 | DosSetMem((PVOID)ulNeedsCommit, 4096,
|
---|
530 | PAG_COMMIT | PAG_READ | PAG_WRITE);
|
---|
531 | // advance the address (only if nobody has already done so -- they
|
---|
532 | // could already commit more than we did)
|
---|
533 | __atomic_cmpxchg32((volatile uint32_t *)&pHdr->ulNeedsCommit,
|
---|
534 | ulNeedsCommit + 4096, ulNeedsCommit);
|
---|
535 | }
|
---|
536 |
|
---|
537 | if (__atomic_cmpxchg32((volatile uint32_t *)ulCurr,
|
---|
538 | pIconData->hwnd, NULLHANDLE))
|
---|
539 | break;
|
---|
540 |
|
---|
541 | ulCurr += sizeof(MEMPOOLBLK);
|
---|
542 | if (ulCurr > ulMax)
|
---|
543 | // start over
|
---|
544 | ulCurr = ((ULONG)pvMemoryPool) + sizeof(MEMPOOLHDR);
|
---|
545 |
|
---|
546 | if (ulCurr == ulNext)
|
---|
547 | return NULL; // no free blocks!
|
---|
548 | }
|
---|
549 | while (1);
|
---|
550 |
|
---|
551 | LOGF(("ulCurr %p\n", ulCurr));
|
---|
552 |
|
---|
553 | // memorize a pointer to the new struct
|
---|
554 | pData = &((PMEMPOOLBLK)ulCurr)->NotifyData;
|
---|
555 |
|
---|
556 | // advance to the next possibly free block
|
---|
557 | ulCurr += sizeof(MEMPOOLBLK);
|
---|
558 | if (ulCurr > ulMax)
|
---|
559 | // start over
|
---|
560 | ulCurr = ((ULONG)pvMemoryPool) + sizeof(MEMPOOLHDR);
|
---|
561 |
|
---|
562 | // store the new next address until someone else has already done that
|
---|
563 | __atomic_cmpxchg32((volatile uint32_t *)&pHdr->ulNext, ulCurr, ulNext);
|
---|
564 |
|
---|
565 | // fill in parts of the allocated NOTIFYDATA
|
---|
566 | memset(pData, 0, sizeof(*pData));
|
---|
567 |
|
---|
568 | pData->msg = pIconData->ulMsgId;
|
---|
569 | pData->mp1 = MPFROMSHORT(pIconData->usId);
|
---|
570 |
|
---|
571 | return pData;
|
---|
572 | }
|
---|
573 |
|
---|
574 | /*
|
---|
575 | *@@ PostNotifyMsg:
|
---|
576 | * Posts WM_XST_NOTIFY to the given client window. Frees pNotifyData if
|
---|
577 | * posting fails.
|
---|
578 | */
|
---|
579 |
|
---|
580 | VOID PostNotifyMsg(PSYSTRAYDATA pSysTrayData, HWND hwnd,
|
---|
581 | PNOTIFYDATA pNotifyData)
|
---|
582 | {
|
---|
583 | LOGF(("hwnd %lx\n", hwnd));
|
---|
584 | LOGF(("pNotifyData %p\n", pNotifyData));
|
---|
585 |
|
---|
586 | if (!WinPostMsg(hwnd, WM_XST_NOTIFY, pNotifyData, pSysTrayData->pvMemoryPool))
|
---|
587 | {
|
---|
588 | LOGF(("WinPostMsg() failed, last error %lx\n", WinGetLastError(0)));
|
---|
589 | FreeNotifyDataPtr(pSysTrayData->pvMemoryPool, hwnd, pNotifyData);
|
---|
590 | }
|
---|
591 | }
|
---|
592 |
|
---|
593 | /*
|
---|
594 | *@@ DrawPointer:
|
---|
595 | * Draws a pointer in a presentation space.
|
---|
596 | */
|
---|
597 |
|
---|
598 | static
|
---|
599 | BOOL DrawPointer(HPS hps, LONG lx, LONG ly, HPOINTER hptrPointer, BOOL bMini)
|
---|
600 | {
|
---|
601 | return WinDrawPointer(hps, lx, ly, hptrPointer, bMini ? DP_MINI : DP_NORMAL);
|
---|
602 | // @todo: for icons with real alpha, do manual alpha blending
|
---|
603 | }
|
---|
604 |
|
---|
605 | /* ******************************************************************
|
---|
606 | *
|
---|
607 | * PM window class implementation
|
---|
608 | *
|
---|
609 | ********************************************************************/
|
---|
610 |
|
---|
611 | /*
|
---|
612 | * This code has the actual PM window class.
|
---|
613 | *
|
---|
614 | */
|
---|
615 |
|
---|
616 | /*
|
---|
617 | *@@ MwgtControl:
|
---|
618 | * implementation for WM_CONTROL in fnwpXSysTray.
|
---|
619 | *
|
---|
620 | * The XCenter communicates with widgets thru
|
---|
621 | * WM_CONTROL messages. At the very least, the
|
---|
622 | * widget should respond to XN_QUERYSIZE because
|
---|
623 | * otherwise it will be given some dumb default
|
---|
624 | * size.
|
---|
625 | */
|
---|
626 |
|
---|
627 | static
|
---|
628 | BOOL WgtControl(PXCENTERWIDGET pWidget,
|
---|
629 | MPARAM mp1,
|
---|
630 | MPARAM mp2)
|
---|
631 | {
|
---|
632 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
633 | BOOL brc = FALSE;
|
---|
634 |
|
---|
635 | USHORT usID = SHORT1FROMMP(mp1),
|
---|
636 | usNotifyCode = SHORT2FROMMP(mp1);
|
---|
637 |
|
---|
638 | // is this from the XCenter client?
|
---|
639 | if (usID == ID_XCENTER_CLIENT)
|
---|
640 | {
|
---|
641 | // yes:
|
---|
642 |
|
---|
643 | switch (usNotifyCode)
|
---|
644 | {
|
---|
645 | /*
|
---|
646 | * XN_QUERYSIZE:
|
---|
647 | * XCenter wants to know our size.
|
---|
648 | */
|
---|
649 |
|
---|
650 | case XN_QUERYSIZE:
|
---|
651 | {
|
---|
652 | PSIZEL pszl = (PSIZEL)mp2;
|
---|
653 | LONG pad = pSysTrayData->lIconPad;
|
---|
654 | size_t cnt = pSysTrayData->cIcons;
|
---|
655 | // desired width
|
---|
656 | if (cnt)
|
---|
657 | pszl->cx = pad + (pSysTrayData->lIconWidth + pad) * cnt;
|
---|
658 | else
|
---|
659 | pszl->cx = pad;
|
---|
660 | // desired minimum height
|
---|
661 | pszl->cy = pSysTrayData->lIconHeight + pad * 2;
|
---|
662 | brc = TRUE;
|
---|
663 | }
|
---|
664 | break;
|
---|
665 |
|
---|
666 | }
|
---|
667 | }
|
---|
668 | else if (usID == ID_XCENTER_TOOLTIP)
|
---|
669 | {
|
---|
670 | PICONDATA pIconData;
|
---|
671 | POINTL ptl;
|
---|
672 |
|
---|
673 | WinQueryMsgPos(pWidget->habWidget, &ptl);
|
---|
674 | // make the coordinates systray-relative
|
---|
675 | WinMapWindowPoints(HWND_DESKTOP, pWidget->hwndWidget, &ptl, 1);
|
---|
676 |
|
---|
677 | pIconData = FindIconDataAtPt(pWidget, &ptl, NULL);
|
---|
678 |
|
---|
679 | switch (usNotifyCode)
|
---|
680 | {
|
---|
681 | case TTN_NEEDTEXT:
|
---|
682 | {
|
---|
683 | LOGF(("TTN_NEEDTEXT\n"));
|
---|
684 |
|
---|
685 | PTOOLTIPTEXT pttt = (PTOOLTIPTEXT)mp2;
|
---|
686 | pttt->ulFormat = TTFMT_PSZ;
|
---|
687 |
|
---|
688 | if (!pIconData || !pIconData->pszToolTip)
|
---|
689 | {
|
---|
690 | pttt->pszText = pSysTrayData->cIcons ?
|
---|
691 | "Indicator area" : "Indicator area (empty)";
|
---|
692 | }
|
---|
693 | else
|
---|
694 | {
|
---|
695 | strncpy(pSysTrayData->szToolTip, pIconData->pszToolTip,
|
---|
696 | sizeof(pSysTrayData->szToolTip) - 1);
|
---|
697 | // be on the safe side
|
---|
698 | pSysTrayData->szToolTip[sizeof(pSysTrayData->szToolTip) - 1] = '\0';
|
---|
699 |
|
---|
700 | pttt->pszText = pSysTrayData->szToolTip;
|
---|
701 | }
|
---|
702 |
|
---|
703 | LOGF((" pszText '%s'\n", pttt->pszText));
|
---|
704 | }
|
---|
705 | break;
|
---|
706 |
|
---|
707 | case TTN_SHOW:
|
---|
708 | if (pIconData)
|
---|
709 | pIconData->bIsToolTipShowing = TRUE;
|
---|
710 | break;
|
---|
711 |
|
---|
712 | case TTN_POP:
|
---|
713 | if (pIconData)
|
---|
714 | pIconData->bIsToolTipShowing = FALSE;
|
---|
715 | break;
|
---|
716 | }
|
---|
717 | }
|
---|
718 |
|
---|
719 | return brc;
|
---|
720 | }
|
---|
721 |
|
---|
722 | /*
|
---|
723 | *@@ WgtPaint:
|
---|
724 | * implementation for WM_PAINT in fnwpXSysTray.
|
---|
725 | *
|
---|
726 | * Draws all the icons. If the widget's center is located to the left from
|
---|
727 | * the XCenter's center, icons go left to right. Otherwise, they go right
|
---|
728 | * to left.
|
---|
729 | *
|
---|
730 | * NOTE: This function must be keept in sync with FindIconDataAtPt() and
|
---|
731 | * SYSTRAYCMD_QUERYRECT in terms of system tray geometry.
|
---|
732 | */
|
---|
733 | /*
|
---|
734 | +---------------------------+ p = lIconPad
|
---|
735 | | p | w = lIconWidth
|
---|
736 | | +-------+ +-------+ | h = lIconHeight
|
---|
737 | | p | w | p | w | p |
|
---|
738 | | | h| | h| |
|
---|
739 | | | | | | | If "Frame around statics" is on in XCenter
|
---|
740 | | +-------+ +-------+ | properties, then a 1 px 3D frame is drawn
|
---|
741 | | p | within the pad area. So, lIconPad must
|
---|
742 | +---------------------------+ be at least 2 px.
|
---|
743 | */
|
---|
744 |
|
---|
745 | static
|
---|
746 | VOID WgtPaint(HWND hwnd,
|
---|
747 | PXCENTERWIDGET pWidget)
|
---|
748 | {
|
---|
749 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
750 | HPS hps;
|
---|
751 | RECTL rclPaint;
|
---|
752 |
|
---|
753 | if ((hps = WinBeginPaint(hwnd, NULLHANDLE, &rclPaint)))
|
---|
754 | {
|
---|
755 | SWP swp;
|
---|
756 | RECTL rcl;
|
---|
757 | BOOL bLeftToRight;
|
---|
758 | LONG x, y, lIconStep;
|
---|
759 | size_t i;
|
---|
760 |
|
---|
761 | WinQueryWindowPos(hwnd, &swp);
|
---|
762 | WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
|
---|
763 |
|
---|
764 | // correct the paint area
|
---|
765 | // @todo find out why it exceeds the window bounds
|
---|
766 | if (rclPaint.xLeft < 0)
|
---|
767 | rclPaint.xLeft = 0;
|
---|
768 | if (rclPaint.xRight > swp.cx)
|
---|
769 | rclPaint.xRight = swp.cx;
|
---|
770 | if (rclPaint.yBottom < 0)
|
---|
771 | rclPaint.yBottom = 0;
|
---|
772 | if (rclPaint.yTop > swp.cy)
|
---|
773 | rclPaint.yTop = swp.cy;
|
---|
774 |
|
---|
775 | LOGF(("rclPaint %d,%d-%d,%d\n",
|
---|
776 | rclPaint.xLeft, rclPaint.yBottom, rclPaint.xRight, rclPaint.yTop));
|
---|
777 |
|
---|
778 | // switch HPS to RGB mode
|
---|
779 | GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
|
---|
780 |
|
---|
781 | // draw icons left to right if our center is closer to the left edge
|
---|
782 | // of XCenter and right to left otherwise
|
---|
783 | bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
|
---|
784 |
|
---|
785 | WinFillRect(hps, &rclPaint,
|
---|
786 | WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0));
|
---|
787 |
|
---|
788 | if ((pWidget->pGlobals->flDisplayStyle & XCS_SUNKBORDERS))
|
---|
789 | {
|
---|
790 | rcl.xLeft = 0;
|
---|
791 | rcl.yBottom = 0;
|
---|
792 | rcl.xRight = swp.cx - 1;
|
---|
793 | rcl.yTop = swp.cy - 1;
|
---|
794 | pgpihDraw3DFrame(hps, &rcl, 1,
|
---|
795 | pWidget->pGlobals->lcol3DDark,
|
---|
796 | pWidget->pGlobals->lcol3DLight);
|
---|
797 | }
|
---|
798 |
|
---|
799 | // always center the icon vertically (we may be given more height than
|
---|
800 | // we requested)
|
---|
801 | y = (swp.cy - pSysTrayData->lIconHeight) / 2;
|
---|
802 |
|
---|
803 | if (bLeftToRight)
|
---|
804 | x = pSysTrayData->lIconPad;
|
---|
805 | else
|
---|
806 | x = swp.cx - pSysTrayData->lIconPad - pSysTrayData->lIconWidth;
|
---|
807 |
|
---|
808 | lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
|
---|
809 |
|
---|
810 | // where to start from?
|
---|
811 | if (bLeftToRight)
|
---|
812 | {
|
---|
813 | i = rclPaint.xLeft / lIconStep;
|
---|
814 | x = pSysTrayData->lIconPad + i * lIconStep;
|
---|
815 | }
|
---|
816 | else
|
---|
817 | {
|
---|
818 | i = (swp.cx - rclPaint.xRight) / lIconStep;
|
---|
819 | x = swp.cx - (i + 1) * lIconStep;
|
---|
820 | // negate the step, for convenience
|
---|
821 | lIconStep = -lIconStep;
|
---|
822 | }
|
---|
823 |
|
---|
824 | // draw as many icons as we can / need
|
---|
825 | for (; i < pSysTrayData->cIcons; ++i)
|
---|
826 | {
|
---|
827 | if (x >= rclPaint.xRight)
|
---|
828 | break;
|
---|
829 |
|
---|
830 | // just leave an empty box if the icon is NULL, this is what
|
---|
831 | // Windows and Linux tray widgets do
|
---|
832 | if (pSysTrayData->pIcons[i].hIcon != NULLHANDLE)
|
---|
833 | DrawPointer(hps, x, y, pSysTrayData->pIcons[i].hIcon, DP_MINI);
|
---|
834 | x += lIconStep;
|
---|
835 | }
|
---|
836 |
|
---|
837 | WinEndPaint(hps);
|
---|
838 | }
|
---|
839 | }
|
---|
840 |
|
---|
841 | /*
|
---|
842 | *@@ WgtMouse:
|
---|
843 | * implementation for WM_BUTTONxyyy in fnwpXSysTray.
|
---|
844 | *
|
---|
845 | * Posts a notification to the window associated with the icon and returns
|
---|
846 | * TRUE if this mouse message is within the icon bounds. Otherwise returns
|
---|
847 | * FALSE.
|
---|
848 | *
|
---|
849 | * Refer to WgtPaint for more details about the widget geometry.
|
---|
850 | */
|
---|
851 |
|
---|
852 | static
|
---|
853 | BOOL WgtMouse(HWND hwnd, ULONG msg, MRESULT mp1, MRESULT mp2,
|
---|
854 | PXCENTERWIDGET pWidget)
|
---|
855 | {
|
---|
856 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
857 |
|
---|
858 | POINTL ptl;
|
---|
859 |
|
---|
860 | PICONDATA pIconData;
|
---|
861 | PNOTIFYDATA pNotifyData;
|
---|
862 |
|
---|
863 | PPOINTS ppts = (PPOINTS)&mp1;
|
---|
864 | ptl.x = ppts->x;
|
---|
865 | ptl.y = ppts->y;
|
---|
866 |
|
---|
867 | LOGF(("msg %x ptl %ld,%ld\n", msg, ptl.x, ptl.y));
|
---|
868 |
|
---|
869 | pIconData = FindIconDataAtPt(pWidget, &ptl, NULL);
|
---|
870 | if (!pIconData)
|
---|
871 | return FALSE; // hit pad space
|
---|
872 |
|
---|
873 | LOGF(("hwnd %x\n", pIconData->hwnd));
|
---|
874 | LOGF(("usId %d\n", pIconData->usId));
|
---|
875 | LOGF(("hIcon %x\n", pIconData->hIcon));
|
---|
876 |
|
---|
877 | // make the coordinates global
|
---|
878 | WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
|
---|
879 |
|
---|
880 | // allocate a NOTIFYDATA struct
|
---|
881 | pNotifyData = AllocNotifyDataPtr(pSysTrayData->pvMemoryPool, pIconData);
|
---|
882 | if (!pNotifyData)
|
---|
883 | return FALSE;
|
---|
884 |
|
---|
885 | switch (msg)
|
---|
886 | {
|
---|
887 | case WM_HSCROLL:
|
---|
888 | case WM_VSCROLL:
|
---|
889 | pNotifyData->mp1 += XST_IN_WHEEL << 16;
|
---|
890 | pNotifyData->u.WheelMsg.ulWheelMsg = msg;
|
---|
891 | pNotifyData->u.WheelMsg.ptsPointerPos.x = ptl.x;
|
---|
892 | pNotifyData->u.WheelMsg.ptsPointerPos.y = ptl.y;
|
---|
893 | pNotifyData->u.WheelMsg.usCmd = SHORT2FROMMP(mp2);
|
---|
894 | pNotifyData->mp2 = &pNotifyData->u.WheelMsg;
|
---|
895 | break;
|
---|
896 |
|
---|
897 | case WM_CONTEXTMENU:
|
---|
898 | pNotifyData->mp1 += XST_IN_CONTEXT << 16;
|
---|
899 | pNotifyData->u.ContextMsg.ptsPointerPos.x = ptl.x;
|
---|
900 | pNotifyData->u.ContextMsg.ptsPointerPos.y = ptl.y;
|
---|
901 | pNotifyData->u.ContextMsg.fPointer = TRUE;
|
---|
902 | pNotifyData->mp2 = &pNotifyData->u.ContextMsg;
|
---|
903 | break;
|
---|
904 |
|
---|
905 | default:
|
---|
906 | pNotifyData->mp1 += XST_IN_MOUSE << 16;
|
---|
907 | pNotifyData->u.MouseMsg.ulMouseMsg = msg;
|
---|
908 | pNotifyData->u.MouseMsg.ptsPointerPos.x = ptl.x;
|
---|
909 | pNotifyData->u.MouseMsg.ptsPointerPos.y = ptl.y;
|
---|
910 | pNotifyData->u.MouseMsg.fsHitTestRes = SHORT1FROMMP(mp2);
|
---|
911 | pNotifyData->u.MouseMsg.fsFlags = SHORT2FROMMP(mp2);
|
---|
912 | pNotifyData->mp2 = &pNotifyData->u.MouseMsg;
|
---|
913 | break;
|
---|
914 | }
|
---|
915 |
|
---|
916 | PostNotifyMsg(pSysTrayData, pIconData->hwnd, pNotifyData);
|
---|
917 |
|
---|
918 | return TRUE;
|
---|
919 | }
|
---|
920 |
|
---|
921 | /*
|
---|
922 | *@@ fnwpXSysTray:
|
---|
923 | * window procedure for the Extended system tray widget class.
|
---|
924 | *
|
---|
925 | * There are a few rules which widget window procs
|
---|
926 | * must follow. See XCENTERWIDGETCLASS in center.h
|
---|
927 | * for details.
|
---|
928 | *
|
---|
929 | * Other than that, this is a regular window procedure
|
---|
930 | * which follows the basic rules for a PM window class.
|
---|
931 | */
|
---|
932 |
|
---|
933 | static
|
---|
934 | MRESULT EXPENTRY fnwpXSysTray(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
|
---|
935 | {
|
---|
936 | // get widget data from QWL_USER (stored there by WM_CREATE)
|
---|
937 | PXCENTERWIDGET pWidget = (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER);
|
---|
938 | // this ptr is valid after WM_CREATE
|
---|
939 |
|
---|
940 | switch (msg)
|
---|
941 | {
|
---|
942 | /*
|
---|
943 | * WM_CREATE:
|
---|
944 | * as with all widgets, we receive a pointer to the
|
---|
945 | * XCENTERWIDGET in mp1, which was created for us.
|
---|
946 | *
|
---|
947 | * The first thing the widget MUST do on WM_CREATE
|
---|
948 | * is to store the XCENTERWIDGET pointer (from mp1)
|
---|
949 | * in the QWL_USER window word by calling:
|
---|
950 | *
|
---|
951 | * WinSetWindowPtr(hwnd, QWL_USER, mp1);
|
---|
952 | *
|
---|
953 | * We could use XCENTERWIDGET.pUser for allocating
|
---|
954 | * another private memory block for our own stuff,
|
---|
955 | * for example to be able to store fonts and colors.
|
---|
956 | * We ain't doing this in the minimal sample.
|
---|
957 | */
|
---|
958 |
|
---|
959 | case WM_CREATE:
|
---|
960 | {
|
---|
961 | LOGF(("WM_CREATE\n"));
|
---|
962 |
|
---|
963 | PSYSTRAYDATA pSysTrayData = NULL;
|
---|
964 | APIRET arc;
|
---|
965 |
|
---|
966 | WinSetWindowPtr(hwnd, QWL_USER, mp1);
|
---|
967 | if ( (!(pWidget = (PXCENTERWIDGET)mp1))
|
---|
968 | || (!pWidget->pfnwpDefWidgetProc)
|
---|
969 | )
|
---|
970 | // shouldn't happen... stop window creation!!
|
---|
971 | return (MRESULT)TRUE;
|
---|
972 |
|
---|
973 | pSysTrayData = malloc(sizeof(*pSysTrayData));
|
---|
974 | if (pSysTrayData == NULL)
|
---|
975 | return (MRESULT)TRUE;
|
---|
976 |
|
---|
977 | // initialize the SYSTRAYDATA structure
|
---|
978 | memset(pSysTrayData, 0, sizeof(*pSysTrayData));
|
---|
979 | pSysTrayData->lIconWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXICON) / 2;
|
---|
980 | pSysTrayData->lIconHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYICON) / 2;
|
---|
981 | pSysTrayData->lIconPad = pSysTrayData->lIconHeight / 8;
|
---|
982 | pSysTrayData->cIconsMax = ICONARRAY_GROW;
|
---|
983 | pSysTrayData->pIcons = malloc(sizeof(*pSysTrayData->pIcons) *
|
---|
984 | pSysTrayData->cIconsMax);
|
---|
985 | if (pSysTrayData->pIcons == NULL)
|
---|
986 | {
|
---|
987 | FreeSysTrayData(pSysTrayData);
|
---|
988 | return (MRESULT)TRUE;
|
---|
989 | }
|
---|
990 | pSysTrayData->cIcons = 0;
|
---|
991 |
|
---|
992 | // Allocate the memory pool for NOTIFYDATA structs (we don't
|
---|
993 | // PAG_COMMIT all memory, AllocNotifyDataPtr() will do so as needed)
|
---|
994 | arc = DosAllocSharedMem((PVOID)&pSysTrayData->pvMemoryPool, NULL,
|
---|
995 | SERVER_MEMORYPOOL_SIZE,
|
---|
996 | PAG_READ | PAG_WRITE | OBJ_GIVEABLE);
|
---|
997 | if (arc == NO_ERROR)
|
---|
998 | {
|
---|
999 | PMEMPOOLHDR pHdr = (PMEMPOOLHDR)pSysTrayData->pvMemoryPool;
|
---|
1000 | arc = DosSetMem(pSysTrayData->pvMemoryPool, 4096,
|
---|
1001 | PAG_COMMIT | PAG_READ | PAG_WRITE);
|
---|
1002 | if (arc == NO_ERROR)
|
---|
1003 | {
|
---|
1004 | pHdr->ulBeyond = (ULONG)pSysTrayData->pvMemoryPool +
|
---|
1005 | SERVER_MEMORYPOOL_SIZE;
|
---|
1006 | pHdr->ulNeedsCommit = (ULONG)pSysTrayData->pvMemoryPool +
|
---|
1007 | 4096;
|
---|
1008 | pHdr->ulNext = (ULONG)pSysTrayData->pvMemoryPool +
|
---|
1009 | sizeof(MEMPOOLHDR);
|
---|
1010 | }
|
---|
1011 | }
|
---|
1012 | if (arc != NO_ERROR)
|
---|
1013 | {
|
---|
1014 | FreeSysTrayData(pSysTrayData);
|
---|
1015 | return (MRESULT)TRUE;
|
---|
1016 | }
|
---|
1017 |
|
---|
1018 | // create the "server" window (note that we pass the XCENTERWIDGET
|
---|
1019 | // pointer on to it)
|
---|
1020 | pSysTrayData->hwndServer =
|
---|
1021 | WinCreateWindow(HWND_DESKTOP, WNDCLASS_WIDGET_XSYSTRAY_SERVER,
|
---|
1022 | NULL, WS_MINIMIZED,
|
---|
1023 | 0, 0, 0, 0,
|
---|
1024 | HWND_DESKTOP, HWND_BOTTOM,
|
---|
1025 | 0, mp1, NULL);
|
---|
1026 | if (pSysTrayData->hwndServer == NULLHANDLE)
|
---|
1027 | {
|
---|
1028 | FreeSysTrayData(pSysTrayData);
|
---|
1029 | return (MRESULT)TRUE;
|
---|
1030 | }
|
---|
1031 |
|
---|
1032 | pWidget->pUser = pSysTrayData;
|
---|
1033 |
|
---|
1034 | // inform all interested parties that we are fired up
|
---|
1035 | WinBroadcastMsg(HWND_DESKTOP, WM_XST_CREATED,
|
---|
1036 | NULL, NULL, BMSG_POST);
|
---|
1037 |
|
---|
1038 | return FALSE; // confirm success
|
---|
1039 | }
|
---|
1040 | break;
|
---|
1041 |
|
---|
1042 | /*
|
---|
1043 | * WM_DESTROY:
|
---|
1044 | * clean up. This _must_ be passed on to
|
---|
1045 | * ctrDefWidgetProc.
|
---|
1046 | */
|
---|
1047 |
|
---|
1048 | case WM_DESTROY:
|
---|
1049 | {
|
---|
1050 | LOGF(("WM_DESTROY\n"));
|
---|
1051 |
|
---|
1052 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
1053 |
|
---|
1054 | // stop the check alive timer
|
---|
1055 | WinStopTimer(pWidget->habWidget, pSysTrayData->hwndServer,
|
---|
1056 | TID_CHECKALIVE);
|
---|
1057 |
|
---|
1058 | FreeSysTrayData(pSysTrayData);
|
---|
1059 | pWidget->pUser = NULL;
|
---|
1060 |
|
---|
1061 | // We _MUST_ pass this on, or the default widget proc
|
---|
1062 | // cannot clean up, so break
|
---|
1063 | }
|
---|
1064 | break;
|
---|
1065 |
|
---|
1066 | /*
|
---|
1067 | * WM_CONTROL:
|
---|
1068 | * process notifications/queries from the XCenter.
|
---|
1069 | */
|
---|
1070 |
|
---|
1071 | case WM_CONTROL:
|
---|
1072 | return (MPARAM)WgtControl(pWidget, mp1, mp2);
|
---|
1073 | break;
|
---|
1074 |
|
---|
1075 | /*
|
---|
1076 | * WM_PAINT:
|
---|
1077 | * well, paint the widget.
|
---|
1078 | */
|
---|
1079 |
|
---|
1080 | case WM_PAINT:
|
---|
1081 | WgtPaint(hwnd, pWidget);
|
---|
1082 | return (MRESULT)TRUE;
|
---|
1083 | break;
|
---|
1084 |
|
---|
1085 | /*
|
---|
1086 | * WM_PRESPARAMCHANGED:
|
---|
1087 | * A well-behaved widget would intercept
|
---|
1088 | * this and store fonts and colors.
|
---|
1089 | */
|
---|
1090 |
|
---|
1091 | /* case WM_PRESPARAMCHANGED:
|
---|
1092 | break; */
|
---|
1093 |
|
---|
1094 | /*
|
---|
1095 | * All mouse click and wheel events:
|
---|
1096 | * Note that we hide WM_CONTEXTMENU from XCenter when it is within
|
---|
1097 | * the icon bounds as it's a responsibility of the application
|
---|
1098 | * owning the icon to show it.
|
---|
1099 | */
|
---|
1100 |
|
---|
1101 | case WM_BUTTON1UP:
|
---|
1102 | case WM_BUTTON1DOWN:
|
---|
1103 | case WM_BUTTON1CLICK:
|
---|
1104 | case WM_BUTTON1DBLCLK:
|
---|
1105 | case WM_BUTTON2UP:
|
---|
1106 | case WM_BUTTON2DOWN:
|
---|
1107 | case WM_BUTTON2CLICK:
|
---|
1108 | case WM_BUTTON2DBLCLK:
|
---|
1109 | case WM_BUTTON3UP:
|
---|
1110 | case WM_BUTTON3DOWN:
|
---|
1111 | case WM_BUTTON3CLICK:
|
---|
1112 | case WM_BUTTON3DBLCLK:
|
---|
1113 | case WM_CONTEXTMENU:
|
---|
1114 | case WM_VSCROLL:
|
---|
1115 | case WM_HSCROLL:
|
---|
1116 | {
|
---|
1117 | if (WgtMouse(hwnd, msg, mp1, mp2, pWidget))
|
---|
1118 | return (MRESULT)TRUE;
|
---|
1119 | // we didn't hit the icon, pass it on to XCenter
|
---|
1120 | }
|
---|
1121 | break;
|
---|
1122 |
|
---|
1123 | default:
|
---|
1124 | break;
|
---|
1125 |
|
---|
1126 | } // end switch(msg)
|
---|
1127 |
|
---|
1128 | return pWidget->pfnwpDefWidgetProc(hwnd, msg, mp1, mp2);
|
---|
1129 | }
|
---|
1130 |
|
---|
1131 | /*
|
---|
1132 | *@@ WgtXSysTrayUpdateAfterIconAddRemove:
|
---|
1133 | * Name says it all.
|
---|
1134 | */
|
---|
1135 |
|
---|
1136 | static
|
---|
1137 | VOID WgtXSysTrayUpdateAfterIconAddRemove(PXCENTERWIDGET pWidget)
|
---|
1138 | {
|
---|
1139 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
1140 |
|
---|
1141 | if (pSysTrayData->cIcons == 1)
|
---|
1142 | {
|
---|
1143 | // start a timer to perform "is window alive" checks
|
---|
1144 | WinStartTimer(pWidget->habWidget, pSysTrayData->hwndServer,
|
---|
1145 | TID_CHECKALIVE,
|
---|
1146 | TID_CHECKALIVE_TIMEOUT);
|
---|
1147 | }
|
---|
1148 | else
|
---|
1149 | if (pSysTrayData->cIcons == 0)
|
---|
1150 | {
|
---|
1151 | // stop the check alive timer
|
---|
1152 | WinStopTimer(pWidget->habWidget, pSysTrayData->hwndServer,
|
---|
1153 | TID_CHECKALIVE);
|
---|
1154 | }
|
---|
1155 |
|
---|
1156 | // ask XCenter to take our new size into account (this will also
|
---|
1157 | // invalidate us)
|
---|
1158 | WinPostMsg(pWidget->pGlobals->hwndClient,
|
---|
1159 | XCM_REFORMAT,
|
---|
1160 | (MPARAM)XFMF_GETWIDGETSIZES,
|
---|
1161 | 0);
|
---|
1162 | }
|
---|
1163 |
|
---|
1164 | /*
|
---|
1165 | *@@ WgtXSysTrayControl:
|
---|
1166 | * implementation for WM_XST_CONTROL in fnwpXSysTrayServer.
|
---|
1167 | *
|
---|
1168 | * Serves as an entry point for all client-side API requests to the
|
---|
1169 | * Extended system tray.
|
---|
1170 | *
|
---|
1171 | * Note that this message is being sent from another process which is
|
---|
1172 | * awaiting for an answer, so it must return as far as possible (by
|
---|
1173 | * rescheduling any potentially long term operation for another cycle).
|
---|
1174 | */
|
---|
1175 |
|
---|
1176 | static
|
---|
1177 | ULONG WgtXSysTrayControl(HWND hwnd, PXCENTERWIDGET pWidget,
|
---|
1178 | PSYSTRAYCTLDATA pCtlData)
|
---|
1179 | {
|
---|
1180 | BOOL brc = FALSE;
|
---|
1181 | ULONG xrc = XST_FAIL;
|
---|
1182 |
|
---|
1183 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
1184 |
|
---|
1185 | switch (pCtlData->ulCommand)
|
---|
1186 | {
|
---|
1187 | case SYSTRAYCMD_GETVERSION:
|
---|
1188 | {
|
---|
1189 | LOGF(("SYSTRAYCMD_GETVERSION\n"));
|
---|
1190 |
|
---|
1191 | pCtlData->bAcknowledged = TRUE;
|
---|
1192 | pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MAJOR;
|
---|
1193 | pCtlData->u.version.ulMinor = XSYSTRAY_VERSION_MINOR;
|
---|
1194 | pCtlData->u.version.ulRevision = XSYSTRAY_VERSION_REVISION;
|
---|
1195 | xrc = XST_OK;
|
---|
1196 | }
|
---|
1197 | break;
|
---|
1198 |
|
---|
1199 | case SYSTRAYCMD_ADDICON:
|
---|
1200 | {
|
---|
1201 | POINTERINFO Info;
|
---|
1202 | HPOINTER hIcon = NULLHANDLE;
|
---|
1203 | size_t i;
|
---|
1204 | PICONDATA pData;
|
---|
1205 |
|
---|
1206 | LOGF(("SYSTRAYCMD_ADDICON\n"));
|
---|
1207 | LOGF((" hwnd %x\n", pCtlData->hwndSender));
|
---|
1208 | LOGF((" usId %d\n", pCtlData->u.icon.usId));
|
---|
1209 | LOGF((" hIcon %x\n", pCtlData->u.icon.hIcon));
|
---|
1210 | LOGF((" szToolTip '%s'\n", pCtlData->u.icon.szToolTip));
|
---|
1211 |
|
---|
1212 | pCtlData->bAcknowledged = TRUE;
|
---|
1213 |
|
---|
1214 | // make a private copy of the provided icon (it will get lost after
|
---|
1215 | // we return from this message)
|
---|
1216 | if (pCtlData->u.icon.hIcon != NULLHANDLE)
|
---|
1217 | {
|
---|
1218 | brc = WinQueryPointerInfo(pCtlData->u.icon.hIcon, &Info);
|
---|
1219 | if (!brc)
|
---|
1220 | break;
|
---|
1221 | hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &Info);
|
---|
1222 | if (hIcon == NULLHANDLE)
|
---|
1223 | break;
|
---|
1224 | }
|
---|
1225 |
|
---|
1226 | pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
|
---|
1227 | pCtlData->u.icon.usId, &i);
|
---|
1228 | if (pData)
|
---|
1229 | {
|
---|
1230 | LOGF((" Replacing with hIcon %x\n", hIcon));
|
---|
1231 |
|
---|
1232 | // try update the tooltip first
|
---|
1233 | free(pData->pszToolTip);
|
---|
1234 | pData->pszToolTip = NULL;
|
---|
1235 | if (pCtlData->u.icon.szToolTip[0] != '\0')
|
---|
1236 | {
|
---|
1237 | pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
|
---|
1238 | if (!pData->pszToolTip)
|
---|
1239 | {
|
---|
1240 | if (hIcon != NULLHANDLE)
|
---|
1241 | WinDestroyPointer(hIcon);
|
---|
1242 | break;
|
---|
1243 | }
|
---|
1244 | }
|
---|
1245 |
|
---|
1246 | if (pData->bIsToolTipShowing)
|
---|
1247 | {
|
---|
1248 | if (pData->pszToolTip)
|
---|
1249 | // update the tooltip on screen
|
---|
1250 | WinSendMsg(pWidget->pGlobals->hwndTooltip,
|
---|
1251 | TTM_UPDATETIPTEXT,
|
---|
1252 | (MPARAM)pData->pszToolTip, 0);
|
---|
1253 | else
|
---|
1254 | // hide the tooltip
|
---|
1255 | WinSendMsg(pWidget->pGlobals->hwndTooltip,
|
---|
1256 | TTM_SHOWTOOLTIPNOW,
|
---|
1257 | (MPARAM)FALSE, 0);
|
---|
1258 | }
|
---|
1259 |
|
---|
1260 | // now update the icon
|
---|
1261 | if (pData->hIcon != NULLHANDLE)
|
---|
1262 | WinDestroyPointer(pData->hIcon);
|
---|
1263 | pData->hIcon = hIcon;
|
---|
1264 | pData->ulMsgId = pCtlData->u.icon.ulMsgId;
|
---|
1265 |
|
---|
1266 | // we didn't change the number of icons so simply invalidate
|
---|
1267 | WinInvalidateRect(pWidget->hwndWidget, NULL, FALSE);
|
---|
1268 |
|
---|
1269 | xrc = XST_REPLACED;
|
---|
1270 | }
|
---|
1271 | else
|
---|
1272 | {
|
---|
1273 | LOGF((" Adding new hIcon %x\n", hIcon));
|
---|
1274 |
|
---|
1275 | if (pSysTrayData->cIcons == pSysTrayData->cIconsMax)
|
---|
1276 | {
|
---|
1277 | PICONDATA pNewIcons;
|
---|
1278 |
|
---|
1279 | LOGF((" Allocating more memory (new icon count is %u)!\n",
|
---|
1280 | pSysTrayData->cIcons + 1));
|
---|
1281 |
|
---|
1282 | pNewIcons = realloc(pSysTrayData->pIcons,
|
---|
1283 | sizeof(*pSysTrayData->pIcons) *
|
---|
1284 | pSysTrayData->cIconsMax + ICONARRAY_GROW);
|
---|
1285 | if (pNewIcons == NULL)
|
---|
1286 | {
|
---|
1287 | if (hIcon != NULLHANDLE)
|
---|
1288 | WinDestroyPointer(hIcon);
|
---|
1289 | break;
|
---|
1290 | }
|
---|
1291 |
|
---|
1292 | pSysTrayData->pIcons = pNewIcons;
|
---|
1293 | pSysTrayData->cIconsMax += ICONARRAY_GROW;
|
---|
1294 | }
|
---|
1295 |
|
---|
1296 | i = pSysTrayData->cIcons;
|
---|
1297 |
|
---|
1298 | pData = &pSysTrayData->pIcons[i];
|
---|
1299 | memset(pData, 0, sizeof(*pData));
|
---|
1300 |
|
---|
1301 | pData->hwnd = pCtlData->hwndSender;
|
---|
1302 | pData->usId = pCtlData->u.icon.usId;
|
---|
1303 | pData->hIcon = hIcon;
|
---|
1304 | pData->ulMsgId = pCtlData->u.icon.ulMsgId;
|
---|
1305 |
|
---|
1306 | if (pCtlData->u.icon.szToolTip[0] != '\0')
|
---|
1307 | {
|
---|
1308 | pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
|
---|
1309 | if (!pData->pszToolTip)
|
---|
1310 | {
|
---|
1311 | if (hIcon != NULLHANDLE)
|
---|
1312 | WinDestroyPointer(hIcon);
|
---|
1313 | break;
|
---|
1314 | }
|
---|
1315 | }
|
---|
1316 |
|
---|
1317 | ++pSysTrayData->cIcons;
|
---|
1318 |
|
---|
1319 | WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
|
---|
1320 |
|
---|
1321 | xrc = XST_OK;
|
---|
1322 | }
|
---|
1323 | }
|
---|
1324 | break;
|
---|
1325 |
|
---|
1326 | case SYSTRAYCMD_REPLACEICON:
|
---|
1327 | {
|
---|
1328 | POINTERINFO Info;
|
---|
1329 | HPOINTER hIcon = NULLHANDLE;
|
---|
1330 | size_t i;
|
---|
1331 | PICONDATA pData;
|
---|
1332 |
|
---|
1333 | LOGF(("SYSTRAYCMD_REPLACEICON\n"));
|
---|
1334 | LOGF((" hwnd %x\n", pCtlData->hwndSender));
|
---|
1335 | LOGF((" usId %d\n", pCtlData->u.icon.usId));
|
---|
1336 |
|
---|
1337 | pCtlData->bAcknowledged = TRUE;
|
---|
1338 |
|
---|
1339 | // make a private copy of the provided icon (it will get lost after
|
---|
1340 | // we return from this message)
|
---|
1341 | if (pCtlData->u.icon.hIcon != NULLHANDLE)
|
---|
1342 | {
|
---|
1343 | brc = WinQueryPointerInfo(pCtlData->u.icon.hIcon, &Info);
|
---|
1344 | if (!brc)
|
---|
1345 | break;
|
---|
1346 | hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &Info);
|
---|
1347 | if (hIcon == NULLHANDLE)
|
---|
1348 | break;
|
---|
1349 | }
|
---|
1350 |
|
---|
1351 | pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
|
---|
1352 | pCtlData->u.icon.usId, &i);
|
---|
1353 | if (pData)
|
---|
1354 | {
|
---|
1355 | LOGF((" Replacing with hIcon %x\n", hIcon));
|
---|
1356 |
|
---|
1357 | if (pData->hIcon != NULLHANDLE)
|
---|
1358 | WinDestroyPointer(pData->hIcon);
|
---|
1359 | pData->hIcon = hIcon;
|
---|
1360 | pData->ulMsgId = pCtlData->u.icon.ulMsgId;
|
---|
1361 |
|
---|
1362 | // we didn't change the number of icons so simply invalidate
|
---|
1363 | WinInvalidateRect(pWidget->hwndWidget, NULL, FALSE);
|
---|
1364 |
|
---|
1365 | xrc = XST_OK;
|
---|
1366 | }
|
---|
1367 | else
|
---|
1368 | LOGF((" Icon not found!\n"));
|
---|
1369 | }
|
---|
1370 | break;
|
---|
1371 |
|
---|
1372 | case SYSTRAYCMD_REMOVEICON:
|
---|
1373 | {
|
---|
1374 | size_t i;
|
---|
1375 | PICONDATA pData;
|
---|
1376 |
|
---|
1377 | LOGF(("SYSTRAYCMD_REMOVEICON\n"));
|
---|
1378 | LOGF((" hwnd %x\n", pCtlData->hwndSender));
|
---|
1379 | LOGF((" usId %d\n", pCtlData->u.icon.usId));
|
---|
1380 |
|
---|
1381 | pCtlData->bAcknowledged = TRUE;
|
---|
1382 |
|
---|
1383 | pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
|
---|
1384 | pCtlData->u.icon.usId, &i);
|
---|
1385 | if (pData)
|
---|
1386 | {
|
---|
1387 | LOGF((" Removing hIcon %x\n", pData->hIcon));
|
---|
1388 |
|
---|
1389 | FreeIconData(pData);
|
---|
1390 |
|
---|
1391 | --pSysTrayData->cIcons;
|
---|
1392 | if (pSysTrayData->cIcons > 0)
|
---|
1393 | {
|
---|
1394 | memcpy(&pSysTrayData->pIcons[i],
|
---|
1395 | &pSysTrayData->pIcons[i + 1],
|
---|
1396 | sizeof(*pSysTrayData->pIcons) * (pSysTrayData->cIcons - i));
|
---|
1397 | }
|
---|
1398 |
|
---|
1399 | WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
|
---|
1400 |
|
---|
1401 | xrc = XST_OK;
|
---|
1402 | }
|
---|
1403 | else
|
---|
1404 | LOGF((" Icon not found!\n"));
|
---|
1405 | }
|
---|
1406 | break;
|
---|
1407 |
|
---|
1408 | case SYSTRAYCMD_SETTOOLTIP:
|
---|
1409 | {
|
---|
1410 | size_t i;
|
---|
1411 | PICONDATA pData;
|
---|
1412 |
|
---|
1413 | LOGF(("SYSTRAYCMD_SETTOOLTIP\n"));
|
---|
1414 | LOGF((" hwnd %x\n", pCtlData->hwndSender));
|
---|
1415 | LOGF((" usId %d\n", pCtlData->u.icon.usId));
|
---|
1416 |
|
---|
1417 | pCtlData->bAcknowledged = TRUE;
|
---|
1418 |
|
---|
1419 | pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
|
---|
1420 | pCtlData->u.icon.usId, &i);
|
---|
1421 | if (pData)
|
---|
1422 | {
|
---|
1423 | LOGF((" Replacing with szToolTip '%s'\n",
|
---|
1424 | pCtlData->u.icon.szToolTip));
|
---|
1425 |
|
---|
1426 | free(pData->pszToolTip);
|
---|
1427 | pData->pszToolTip = NULL;
|
---|
1428 | if (pCtlData->u.icon.szToolTip[0] != '\0')
|
---|
1429 | {
|
---|
1430 | pData->pszToolTip = strdup(pCtlData->u.icon.szToolTip);
|
---|
1431 | if (!pData->pszToolTip)
|
---|
1432 | break;
|
---|
1433 | }
|
---|
1434 |
|
---|
1435 | if (pData->bIsToolTipShowing)
|
---|
1436 | {
|
---|
1437 | if (pData->pszToolTip)
|
---|
1438 | // update the tooltip on screen
|
---|
1439 | WinSendMsg(pWidget->pGlobals->hwndTooltip,
|
---|
1440 | TTM_UPDATETIPTEXT,
|
---|
1441 | (MPARAM)pData->pszToolTip, 0);
|
---|
1442 | else
|
---|
1443 | // hide the tooltip
|
---|
1444 | WinSendMsg(pWidget->pGlobals->hwndTooltip,
|
---|
1445 | TTM_SHOWTOOLTIPNOW,
|
---|
1446 | (MPARAM)FALSE, 0);
|
---|
1447 | }
|
---|
1448 |
|
---|
1449 | xrc = XST_OK;
|
---|
1450 | }
|
---|
1451 | else
|
---|
1452 | LOGF((" Icon not found!\n"));
|
---|
1453 | }
|
---|
1454 | break;
|
---|
1455 |
|
---|
1456 | case SYSTRAYCMD_QUERYRECT:
|
---|
1457 | {
|
---|
1458 | size_t i;
|
---|
1459 | PICONDATA pData;
|
---|
1460 |
|
---|
1461 | LOGF(("SYSTRAYCMD_QUERYRECT\n"));
|
---|
1462 | LOGF((" hwnd %x\n", pCtlData->hwndSender));
|
---|
1463 | LOGF((" usId %d\n", pCtlData->u.icon.usId));
|
---|
1464 |
|
---|
1465 | pCtlData->bAcknowledged = TRUE;
|
---|
1466 |
|
---|
1467 | pData = FindIconData(pSysTrayData, pCtlData->hwndSender,
|
---|
1468 | pCtlData->u.icon.usId, &i);
|
---|
1469 | if (pData)
|
---|
1470 | {
|
---|
1471 | // Refer to FindIconDataAtPt() for details
|
---|
1472 |
|
---|
1473 | SWP swp;
|
---|
1474 | RECTL rcl;
|
---|
1475 | BOOL bLeftToRight;
|
---|
1476 | LONG y, lIconStep;
|
---|
1477 |
|
---|
1478 | WinQueryWindowPos(pWidget->hwndWidget, &swp);
|
---|
1479 | WinQueryWindowRect(pWidget->pGlobals->hwndClient, &rcl);
|
---|
1480 |
|
---|
1481 | y = (swp.cy - pSysTrayData->lIconHeight) / 2;
|
---|
1482 |
|
---|
1483 | // detect the direction
|
---|
1484 | bLeftToRight = swp.x + swp.cx / 2 < (rcl.xRight / 2);
|
---|
1485 |
|
---|
1486 | lIconStep = pSysTrayData->lIconWidth + pSysTrayData->lIconPad;
|
---|
1487 |
|
---|
1488 | pCtlData->u.rect.rclIcon.yBottom = y;
|
---|
1489 | pCtlData->u.rect.rclIcon.yTop = y + pSysTrayData->lIconHeight;
|
---|
1490 |
|
---|
1491 | if (bLeftToRight)
|
---|
1492 | {
|
---|
1493 | pCtlData->u.rect.rclIcon.xLeft =
|
---|
1494 | pSysTrayData->lIconPad + (lIconStep) * i;
|
---|
1495 | pCtlData->u.rect.rclIcon.xRight =
|
---|
1496 | pCtlData->u.rect.rclIcon.xLeft +
|
---|
1497 | pSysTrayData->lIconWidth;
|
---|
1498 | }
|
---|
1499 | else
|
---|
1500 | {
|
---|
1501 | pCtlData->u.rect.rclIcon.xLeft =
|
---|
1502 | swp.cx - (lIconStep) * (i + 1);
|
---|
1503 | pCtlData->u.rect.rclIcon.xRight =
|
---|
1504 | pCtlData->u.rect.rclIcon.xLeft +
|
---|
1505 | pSysTrayData->lIconWidth;
|
---|
1506 | }
|
---|
1507 |
|
---|
1508 | // convert to screen coordinates
|
---|
1509 | WinMapWindowPoints(pWidget->hwndWidget, HWND_DESKTOP,
|
---|
1510 | (PPOINTL)&pCtlData->u.rect.rclIcon, 2);
|
---|
1511 | xrc = XST_OK;
|
---|
1512 | }
|
---|
1513 | else
|
---|
1514 | LOGF((" Icon not found!\n"));
|
---|
1515 | }
|
---|
1516 | break;
|
---|
1517 |
|
---|
1518 | default:
|
---|
1519 | break;
|
---|
1520 | }
|
---|
1521 |
|
---|
1522 | LOGF(("return %d (WinGetLastError is %x)\n",
|
---|
1523 | xrc, WinGetLastError(pWidget->habWidget)));
|
---|
1524 |
|
---|
1525 | return xrc;
|
---|
1526 | }
|
---|
1527 |
|
---|
1528 | /*
|
---|
1529 | *@@ WgtXSysTrayTimer:
|
---|
1530 | * implementation for WM_TIMER in fnwpXSysTrayServer.
|
---|
1531 | */
|
---|
1532 |
|
---|
1533 | static
|
---|
1534 | VOID WgtXSysTrayTimer(HWND hwnd, PXCENTERWIDGET pWidget,
|
---|
1535 | USHORT usTimerId)
|
---|
1536 | {
|
---|
1537 | PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
|
---|
1538 | PMEMPOOLHDR pMemPoolHdr = (PMEMPOOLHDR)pSysTrayData->pvMemoryPool;
|
---|
1539 | PMEMPOOLBLK pMemPoolBlk;
|
---|
1540 | ULONG ulMemPoolMax;
|
---|
1541 |
|
---|
1542 | if (usTimerId == TID_CHECKALIVE)
|
---|
1543 | {
|
---|
1544 | // check if windows associated with the icons are still alive
|
---|
1545 | // and remove those icons whose windows are invalid
|
---|
1546 | BOOL bAnyDead = FALSE;
|
---|
1547 | size_t i;
|
---|
1548 | for (i = 0; i < pSysTrayData->cIcons; ++i)
|
---|
1549 | {
|
---|
1550 | if (!WinIsWindow(pWidget->habWidget, pSysTrayData->pIcons[i].hwnd))
|
---|
1551 | {
|
---|
1552 | PICONDATA pData = &pSysTrayData->pIcons[i];
|
---|
1553 |
|
---|
1554 | LOGF(("Removing icon of dead window!\n"));
|
---|
1555 | LOGF((" hwnd %x\n", pData->hwnd));
|
---|
1556 | LOGF((" usId %ld\n", pData->usId));
|
---|
1557 | LOGF((" hIcon %x\n", pData->hIcon));
|
---|
1558 |
|
---|
1559 | // free memory blocks from the pool allocated for this client
|
---|
1560 | ulMemPoolMax = pMemPoolHdr->ulBeyond;
|
---|
1561 | if (ulMemPoolMax > pMemPoolHdr->ulNeedsCommit)
|
---|
1562 | ulMemPoolMax = pMemPoolHdr->ulNeedsCommit;
|
---|
1563 | ulMemPoolMax -= sizeof(MEMPOOLBLK);
|
---|
1564 |
|
---|
1565 | pMemPoolBlk = pMemPoolHdr->aBlocks;
|
---|
1566 | while ((ULONG)pMemPoolBlk <= ulMemPoolMax)
|
---|
1567 | {
|
---|
1568 | if (pMemPoolBlk->hwnd == pData->hwnd)
|
---|
1569 | {
|
---|
1570 | LOGF((" freeing memory block %p\n", pMemPoolBlk));
|
---|
1571 | FreeNotifyDataPtr(pSysTrayData->pvMemoryPool,
|
---|
1572 | pData->hwnd,
|
---|
1573 | &pMemPoolBlk->NotifyData);
|
---|
1574 | }
|
---|
1575 | ++pMemPoolBlk;
|
---|
1576 | }
|
---|
1577 |
|
---|
1578 | bAnyDead = TRUE;
|
---|
1579 | FreeIconData(pData);
|
---|
1580 | // pData->hwnd is NULLHANDLE here
|
---|
1581 | }
|
---|
1582 | }
|
---|
1583 |
|
---|
1584 | if (bAnyDead)
|
---|
1585 | {
|
---|
1586 | // compact the icon array
|
---|
1587 | i = 0;
|
---|
1588 | while (i < pSysTrayData->cIcons)
|
---|
1589 | {
|
---|
1590 | if (pSysTrayData->pIcons[i].hwnd == NULLHANDLE)
|
---|
1591 | {
|
---|
1592 | --pSysTrayData->cIcons;
|
---|
1593 | if (pSysTrayData->cIcons > 0)
|
---|
1594 | {
|
---|
1595 | memcpy(&pSysTrayData->pIcons[i],
|
---|
1596 | &pSysTrayData->pIcons[i + 1],
|
---|
1597 | sizeof(*pSysTrayData->pIcons) * (pSysTrayData->cIcons - i));
|
---|
1598 | }
|
---|
1599 | }
|
---|
1600 | else
|
---|
1601 | ++i;
|
---|
1602 | }
|
---|
1603 |
|
---|
1604 | WgtXSysTrayUpdateAfterIconAddRemove(pWidget);
|
---|
1605 | }
|
---|
1606 | }
|
---|
1607 | }
|
---|
1608 |
|
---|
1609 | /*
|
---|
1610 | *@@ fnwpXSysTrayServer:
|
---|
1611 | * window procedure for the Extended system tray server window class.
|
---|
1612 | *
|
---|
1613 | * A separate "server" class is necessary because we need a CS_FRAME
|
---|
1614 | * top-level window for DDE (which we need to support to be backward
|
---|
1615 | * compatible with the System tray wdget from the SysTray/WPS package) and
|
---|
1616 | * also to make ourselves discoverable for the client-side implementation
|
---|
1617 | * of our new extended API (which queries the class of each top-level
|
---|
1618 | * window to find the system tray server).
|
---|
1619 | */
|
---|
1620 |
|
---|
1621 | static
|
---|
1622 | MRESULT EXPENTRY fnwpXSysTrayServer(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
|
---|
1623 | {
|
---|
1624 | // get widget data from QWL_USER_SERVER_DATA (stored there by WM_CREATE)
|
---|
1625 | PXCENTERWIDGET pWidget =
|
---|
1626 | (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER_SERVER_DATA);
|
---|
1627 | // this ptr is valid after WM_CREATE
|
---|
1628 |
|
---|
1629 | switch (msg)
|
---|
1630 | {
|
---|
1631 | case WM_CREATE:
|
---|
1632 | LOGF(("WM_CREATE\n"));
|
---|
1633 | WinSetWindowPtr(hwnd, QWL_USER_SERVER_DATA, mp1);
|
---|
1634 | return FALSE; // confirm success
|
---|
1635 | break;
|
---|
1636 |
|
---|
1637 | case WM_DESTROY:
|
---|
1638 | LOGF(("WM_DESTROY\n"));
|
---|
1639 | break;
|
---|
1640 |
|
---|
1641 | /*
|
---|
1642 | * WM_XST_CONTROL:
|
---|
1643 | * This is the message sent to us by the clinet-side implementation
|
---|
1644 | * of the API to request some function. mp1 points to a
|
---|
1645 | * SYSTRAYCTLDATA structure.
|
---|
1646 | */
|
---|
1647 |
|
---|
1648 | case WM_XST_CONTROL:
|
---|
1649 | return (MRESULT)WgtXSysTrayControl(hwnd, pWidget,
|
---|
1650 | (PSYSTRAYCTLDATA)mp1);
|
---|
1651 | break;
|
---|
1652 |
|
---|
1653 | /*
|
---|
1654 | * WM_TIMER:
|
---|
1655 | * timer event.
|
---|
1656 | */
|
---|
1657 |
|
---|
1658 | case WM_TIMER:
|
---|
1659 | WgtXSysTrayTimer(hwnd, pWidget, SHORT1FROMMP(mp1));
|
---|
1660 | return (MRESULT)TRUE;
|
---|
1661 | break;
|
---|
1662 |
|
---|
1663 | default:
|
---|
1664 | break;
|
---|
1665 | } // end switch(msg)
|
---|
1666 |
|
---|
1667 | return WinDefWindowProc(hwnd, msg, mp1, mp2);
|
---|
1668 | }
|
---|
1669 |
|
---|
1670 | /* ******************************************************************
|
---|
1671 | *
|
---|
1672 | * Exported procedures
|
---|
1673 | *
|
---|
1674 | ********************************************************************/
|
---|
1675 |
|
---|
1676 | /*
|
---|
1677 | *@@ WgtInitModule:
|
---|
1678 | * required export with ordinal 1, which must tell
|
---|
1679 | * the XCenter how many widgets this DLL provides,
|
---|
1680 | * and give the XCenter an array of XCENTERWIDGETCLASS
|
---|
1681 | * structures describing the widgets.
|
---|
1682 | *
|
---|
1683 | * With this call, you are given the module handle of
|
---|
1684 | * XFLDR.DLL. For convenience, and if you have the full
|
---|
1685 | * XWorkplace source code, you could resolve imports
|
---|
1686 | * for some useful functions which are exported thru
|
---|
1687 | * src\shared\xwp.def. We don't do this here.
|
---|
1688 | *
|
---|
1689 | * This function must also register the PM window classes
|
---|
1690 | * which are specified in the XCENTERWIDGETCLASS array
|
---|
1691 | * entries. For this, you are given a HAB which you
|
---|
1692 | * should pass to WinRegisterClass. For the window
|
---|
1693 | * class style (4th param to WinRegisterClass),
|
---|
1694 | * you should specify
|
---|
1695 | *
|
---|
1696 | + CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT
|
---|
1697 | *
|
---|
1698 | * Your widget window _will_ be resized by the XCenter,
|
---|
1699 | * even if you're not planning it to be.
|
---|
1700 | *
|
---|
1701 | * This function only gets called _once_ when the widget
|
---|
1702 | * DLL has been successfully loaded by the XCenter. If
|
---|
1703 | * there are several instances of a widget running (in
|
---|
1704 | * the same or in several XCenters), this function does
|
---|
1705 | * not get called again. However, since the XCenter unloads
|
---|
1706 | * the widget DLLs again if they are no longer referenced
|
---|
1707 | * by any XCenter, this might get called again when the
|
---|
1708 | * DLL is re-loaded.
|
---|
1709 | *
|
---|
1710 | * There will ever be only one load occurence of the DLL.
|
---|
1711 | * The XCenter manages sharing the DLL between several
|
---|
1712 | * XCenters. As a result, it doesn't matter if the DLL
|
---|
1713 | * has INITINSTANCE etc. set or not.
|
---|
1714 | *
|
---|
1715 | * If this returns 0, this is considered an error, and the
|
---|
1716 | * DLL will be unloaded again immediately.
|
---|
1717 | *
|
---|
1718 | * If this returns any value > 0, *ppaClasses must be
|
---|
1719 | * set to a static array (best placed in the DLL's
|
---|
1720 | * global data) of XCENTERWIDGETCLASS structures,
|
---|
1721 | * which must have as many entries as the return value.
|
---|
1722 | */
|
---|
1723 |
|
---|
1724 | ULONG EXPENTRY WgtInitModule(HAB hab, // XCenter's anchor block
|
---|
1725 | HMODULE hmodPlugin, // module handle of the widget DLL
|
---|
1726 | HMODULE hmodXFLDR, // XFLDR.DLL module handle
|
---|
1727 | PCXCENTERWIDGETCLASS *ppaClasses,
|
---|
1728 | PSZ pszErrorMsg) // if 0 is returned, 500 bytes of error msg
|
---|
1729 | {
|
---|
1730 | ULONG ulrc = 0, ul = 0;
|
---|
1731 | CLASSINFO ClassInfo;
|
---|
1732 |
|
---|
1733 | LOGF(("hmodPlugin %x\n", hmodPlugin));
|
---|
1734 |
|
---|
1735 | do
|
---|
1736 | {
|
---|
1737 | // resolve imports from XFLDR.DLL (this is basically
|
---|
1738 | // a copy of the doshResolveImports code, but we can't
|
---|
1739 | // use that before resolving...)
|
---|
1740 | for (ul = 0;
|
---|
1741 | ul < sizeof(G_aImports) / sizeof(G_aImports[0]);
|
---|
1742 | ul++)
|
---|
1743 | {
|
---|
1744 | APIRET arc;
|
---|
1745 | if ((arc = DosQueryProcAddr(hmodXFLDR,
|
---|
1746 | 0, // ordinal, ignored
|
---|
1747 | (PSZ)G_aImports[ul].pcszFunctionName,
|
---|
1748 | G_aImports[ul].ppFuncAddress))
|
---|
1749 | != NO_ERROR)
|
---|
1750 | {
|
---|
1751 | snprintf(pszErrorMsg, 500,
|
---|
1752 | "Import %s failed with %ld.",
|
---|
1753 | G_aImports[ul].pcszFunctionName, arc);
|
---|
1754 | break;
|
---|
1755 | }
|
---|
1756 | }
|
---|
1757 | if (ul < sizeof(G_aImports) / sizeof(G_aImports[0]))
|
---|
1758 | break;
|
---|
1759 |
|
---|
1760 | // register our PM window class
|
---|
1761 | if (!WinRegisterClass(hab,
|
---|
1762 | WNDCLASS_WIDGET_XSYSTRAY,
|
---|
1763 | fnwpXSysTray,
|
---|
1764 | CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT,
|
---|
1765 | sizeof(PVOID))
|
---|
1766 | // extra memory to reserve for QWL_USER
|
---|
1767 | )
|
---|
1768 | {
|
---|
1769 | snprintf(pszErrorMsg, 500,
|
---|
1770 | "WinRegisterClass(%s) failed with %lX.",
|
---|
1771 | WNDCLASS_WIDGET_XSYSTRAY, WinGetLastError(hab));
|
---|
1772 | break;
|
---|
1773 | }
|
---|
1774 |
|
---|
1775 | // get the window data size for the WC_FRAME class (any window class
|
---|
1776 | // that specifies CS_FRAME must have at least this number, otherise
|
---|
1777 | // WinRegisterClass returns 0x1003
|
---|
1778 | if (!WinQueryClassInfo(hab, (PSZ)WC_FRAME, &ClassInfo))
|
---|
1779 | break;
|
---|
1780 | QWL_USER_SERVER_DATA = ClassInfo.cbWindowData;
|
---|
1781 |
|
---|
1782 | if (!WinRegisterClass(hab,
|
---|
1783 | WNDCLASS_WIDGET_XSYSTRAY_SERVER,
|
---|
1784 | fnwpXSysTrayServer,
|
---|
1785 | CS_FRAME,
|
---|
1786 | QWL_USER_SERVER_DATA + sizeof(PVOID))
|
---|
1787 | // extra memory to reserve for QWL_USER
|
---|
1788 | )
|
---|
1789 | {
|
---|
1790 | // error registering class: report error then
|
---|
1791 | snprintf(pszErrorMsg, 500,
|
---|
1792 | "WinRegisterClass(%s) failed with %lX",
|
---|
1793 | WNDCLASS_WIDGET_XSYSTRAY_SERVER, WinGetLastError(hab));
|
---|
1794 | break;
|
---|
1795 | }
|
---|
1796 |
|
---|
1797 | if (WM_XST_CREATED == 0)
|
---|
1798 | WM_XST_CREATED = WinAddAtom(WinQuerySystemAtomTable(),
|
---|
1799 | WM_XST_CREATED_ATOM);
|
---|
1800 | if (WM_XST_NOTIFY == 0)
|
---|
1801 | WM_XST_NOTIFY = WinAddAtom(WinQuerySystemAtomTable(),
|
---|
1802 | WM_XST_NOTIFY_ATOM);
|
---|
1803 |
|
---|
1804 | // no error:
|
---|
1805 | // return widget classes array
|
---|
1806 | *ppaClasses = G_WidgetClasses;
|
---|
1807 |
|
---|
1808 | // return no. of classes in this DLL (one here):
|
---|
1809 | ulrc = sizeof(G_WidgetClasses) / sizeof(G_WidgetClasses[0]);
|
---|
1810 | }
|
---|
1811 | while (0);
|
---|
1812 |
|
---|
1813 | LOGF(("pszErrorMsg '%s'\n", pszErrorMsg));
|
---|
1814 | LOGF(("ulrc %d\n", ulrc));
|
---|
1815 |
|
---|
1816 | return ulrc;
|
---|
1817 | }
|
---|
1818 |
|
---|
1819 | /*
|
---|
1820 | *@@ WgtUnInitModule:
|
---|
1821 | * optional export with ordinal 2, which can clean
|
---|
1822 | * up global widget class data.
|
---|
1823 | *
|
---|
1824 | * This gets called by the XCenter right before
|
---|
1825 | * a widget DLL gets unloaded. Note that this
|
---|
1826 | * gets called even if the "init module" export
|
---|
1827 | * returned 0 (meaning an error) and the DLL
|
---|
1828 | * gets unloaded right away.
|
---|
1829 | */
|
---|
1830 |
|
---|
1831 | VOID EXPENTRY WgtUnInitModule(VOID)
|
---|
1832 | {
|
---|
1833 | LOGF(("\n"));
|
---|
1834 | }
|
---|
1835 |
|
---|
1836 | /*
|
---|
1837 | *@@ WgtQueryVersion:
|
---|
1838 | * this new export with ordinal 3 can return the
|
---|
1839 | * XWorkplace version number which is required
|
---|
1840 | * for this widget to run. For example, if this
|
---|
1841 | * returns 0.9.10, this widget will not run on
|
---|
1842 | * earlier XWorkplace versions.
|
---|
1843 | *
|
---|
1844 | * NOTE: This export was mainly added because the
|
---|
1845 | * prototype for the "Init" export was changed
|
---|
1846 | * with V0.9.9. If this returns 0.9.9, it is
|
---|
1847 | * assumed that the INIT export understands
|
---|
1848 | * the new FNWGTINITMODULE_099 format (see center.h).
|
---|
1849 | */
|
---|
1850 |
|
---|
1851 | VOID EXPENTRY WgtQueryVersion(PULONG pulMajor,
|
---|
1852 | PULONG pulMinor,
|
---|
1853 | PULONG pulRevision)
|
---|
1854 | {
|
---|
1855 | *pulMajor = 0;
|
---|
1856 | *pulMinor = 9;
|
---|
1857 | *pulRevision = 9;
|
---|
1858 | }
|
---|
1859 |
|
---|