source: branches/4.5.1/src/3rdparty/os2/xsystray/xsystray.c

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

3rdparty: os2/xsystray: Implemented xstQuerySysTrayIconRect() API call. Fixed the regression of the previous checkin (icons wouldn't be removed when the associated application crashed).

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