source: trunk/src/3rdparty/os2/xsystray/plugin/w_xsystray.c@ 1125

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

xsystray: Fix warnings.

File size: 61.8 KB
Line 
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
96typedef 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
121typedef 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
146typedef 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
186static ULONG WM_XST_CREATED = 0;
187 // identity of the WM_XST_CREATED message taken from the atom table
188static ULONG WM_XST_NOTIFY = 0;
189 // identity of the WM_XST_NOTIFY message taken from the atom table
190
191static ULONG QWL_USER_SERVER_DATA = 0;
192 // offset to the PXCENTERWIDGET pointer in the widget data array
193
194static
195VOID 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
214static 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
260typedef VOID XWPENTRY GPIHDRAW3DFRAME(HPS hps,
261 PRECTL prcl,
262 USHORT usWidth,
263 LONG lColorLeft,
264 LONG lColorRight);
265typedef GPIHDRAW3DFRAME *PGPIHDRAW3DFRAME;
266PGPIHDRAW3DFRAME pgpihDraw3DFrame = NULL;
267
268typedef struct _RESOLVEFUNCTION
269{
270 const char *pcszFunctionName;
271 PFN *ppFuncAddress;
272} RESOLVEFUNCTION, *PRESOLVEFUNCTION;
273RESOLVEFUNCTION 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
328static
329VOID 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
353static
354PICONDATA 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
385static
386PICONDATA 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
436static
437VOID 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 */
477static
478PNOTIFYDATA 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
580VOID 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
598static
599BOOL 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
627static
628BOOL 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
745static
746VOID 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
852static
853BOOL 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
933static
934MRESULT 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
1136static
1137VOID 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
1176static
1177ULONG 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
1533static
1534VOID 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
1621static
1622MRESULT 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
1724ULONG 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
1831VOID 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
1851VOID EXPENTRY WgtQueryVersion(PULONG pulMajor,
1852 PULONG pulMinor,
1853 PULONG pulRevision)
1854{
1855 *pulMajor = 0;
1856 *pulMinor = 9;
1857 *pulRevision = 9;
1858}
1859
Note: See TracBrowser for help on using the repository browser.