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

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

gui: xsystray: Further coding (established communication channel between the clients and the server, some bug fixes).

File size: 23.0 KB
Line 
1/*
2 * Extended system tray widget for XCenter/eCenter
3 *
4 * Implementation
5 *
6 * Made by netlabs.org
7 *
8 * Author: Dmitry A. Kuminov
9 *
10 * This software is public domain.
11 *
12 * WITHOUT ANY WARRANTY..., AT YOUR OWN RISC... ETC.
13 *
14 */
15
16/*
17 * This code is based on the code template for a minimal XCenter widget
18 * from the XWorkplace project (c) by Ulrich Moeller.
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// primitive debug logging to a file
78#if 1
79static void __LOG_WORKER(const char *fmt, ...)
80{
81 static FILE *f = NULL;
82 if (f == NULL)
83 {
84 f = fopen("c:\\xsystray.dbg", "a");
85 setbuf(f, NULL);
86 }
87 if (f != NULL)
88 {
89 va_list vl;
90 va_start(vl, fmt);
91 vfprintf(f, fmt, vl);
92 va_end(vl);
93 }
94}
95#define LOG(m) do { __LOG_WORKER m; } while(0)
96#define LOGF(m) do { __LOG_WORKER("%s: ", __FUNCTION__); __LOG_WORKER m; } while(0)
97#else
98#define LOG(m) do {} while (0)
99#define LOGF(m) do {} while (0)
100#endif
101
102/* ******************************************************************
103 *
104 * Private definitions
105 *
106 ********************************************************************/
107
108/*
109 *@@ ICONDATA:
110 * Per-icon data.
111 */
112
113typedef struct _ICONDATA
114{
115 HWND hwnd;
116 // associated window
117 ULONG ulId;
118 // icon ID
119 HPOINTER hIcon;
120 // icon handle
121 ULONG ulMsgId;
122 // message ID for notifications
123 PSZ pszToolTip;
124 // icon tooltip (NULL if none)
125
126} ICONDATA, *PICONDATA;
127
128/*
129 *@@ SYSTRAYDATA:
130 * Global system tray data.
131 */
132
133typedef struct
134{
135 PICONDATA pIcons;
136 // array of icons currently shown in the system tray
137 // (left to right)
138 size_t cIcons;
139 // number of icons in the pIcons array
140 size_t cIconsMax;
141 // maximum number of icons pIcons can fit
142
143} SYSTRAYDATA, *PSYSTRAYDATA;
144
145ULONG QWL_USER_SERVER_DATA = 0;
146
147/* ******************************************************************
148 *
149 * XCenter widget class definition
150 *
151 ********************************************************************/
152
153/*
154 * This contains the name of the PM window class and
155 * the XCENTERWIDGETCLASS definition(s) for the widget
156 * class(es) in this DLL.
157 *
158 * The address of this structure (or an array of these
159 * structures, if there were several widget classes in
160 * this plugin) is returned by the "init" export
161 * (WgtInitModule).
162 */
163
164static const XCENTERWIDGETCLASS G_WidgetClasses[] =
165{
166 {
167 WNDCLASS_WIDGET_XSYSTRAY, // PM window class name
168 0, // additional flag, not used here
169 INTCLASS_WIDGET_XSYSTRAY, // internal widget class name
170 HUMANSTR_WIDGET_XSYSTRAY, // widget class name displayed to user
171 WGTF_UNIQUEGLOBAL, // widget class flags
172 NULL // no settings dialog
173 }
174};
175
176/* ******************************************************************
177 *
178 * Function imports from XFLDR.DLL
179 *
180 ********************************************************************/
181
182/*
183 * To reduce the size of the widget DLL, it can
184 * be compiled with the VAC subsystem libraries.
185 * In addition, instead of linking frequently
186 * used helpers against the DLL again, you can
187 * import them from XFLDR.DLL, whose module handle
188 * is given to you in the INITMODULE export.
189 *
190 * Note that importing functions from XFLDR.DLL
191 * is _not_ a requirement. We can't do this in
192 * this minimal sample anyway without having access
193 * to the full XWorkplace source code.
194 *
195 * If you want to know how you can import the useful
196 * functions from XFLDR.DLL to use them in your widget
197 * plugin, again, see src\widgets in the XWorkplace sources.
198 * The actual imports would then be made by WgtInitModule.
199 */
200
201/* ******************************************************************
202 *
203 * Private widget instance data
204 *
205 ********************************************************************/
206
207// None presently. The samples in src\widgets in the XWorkplace
208// sources cleanly separate setup string data from other widget
209// instance data to allow for easier manipulation with settings
210// dialogs. We have skipped this for the minimal sample.
211
212/* ******************************************************************
213 *
214 * Widget setup management
215 *
216 ********************************************************************/
217
218// None presently. See above.
219
220/* ******************************************************************
221 *
222 * Widget settings dialog
223 *
224 ********************************************************************/
225
226// None currently. To see how a setup dialog can be done,
227// see the "window list" widget in the XWorkplace sources
228// (src\widgets\w_winlist.c).
229
230/* ******************************************************************
231 *
232 * Callbacks stored in XCENTERWIDGETCLASS
233 *
234 ********************************************************************/
235
236// If you implement a settings dialog, you must write a
237// "show settings dlg" function and store its function pointer
238// in XCENTERWIDGETCLASS.
239
240/* ******************************************************************
241 *
242 * PM window class implementation
243 *
244 ********************************************************************/
245
246/*
247 * This code has the actual PM window class.
248 *
249 */
250
251/*
252 *@@ MwgtControl:
253 * implementation for WM_CONTROL in fnwpXSysTray.
254 *
255 * The XCenter communicates with widgets thru
256 * WM_CONTROL messages. At the very least, the
257 * widget should respond to XN_QUERYSIZE because
258 * otherwise it will be given some dumb default
259 * size.
260 */
261
262static
263BOOL WgtControl(PXCENTERWIDGET pWidget,
264 MPARAM mp1,
265 MPARAM mp2)
266{
267 BOOL brc = FALSE;
268
269 USHORT usID = SHORT1FROMMP(mp1),
270 usNotifyCode = SHORT2FROMMP(mp1);
271
272 // is this from the XCenter client?
273 if (usID == ID_XCENTER_CLIENT)
274 {
275 // yes:
276
277 switch (usNotifyCode)
278 {
279 /*
280 * XN_QUERYSIZE:
281 * XCenter wants to know our size.
282 */
283
284 case XN_QUERYSIZE:
285 {
286 PSIZEL pszl = (PSIZEL)mp2;
287 pszl->cx = 30; // desired width
288 pszl->cy = 20; // desired minimum height
289 brc = TRUE;
290 }
291 break;
292
293 } // end switch (usNotifyCode)
294 } // end if (usID == ID_XCENTER_CLIENT)
295
296 return brc;
297}
298
299/*
300 *@@ WgtPaint:
301 * implementation for WM_PAINT in fnwpXSysTray.
302 *
303 * This really does nothing, except painting a
304 * 3D rectangle and printing a question mark.
305 */
306
307static
308VOID WgtPaint(HWND hwnd,
309 PXCENTERWIDGET pWidget)
310{
311 HPS hps;
312 if ((hps = WinBeginPaint(hwnd, NULLHANDLE, NULL)))
313 {
314 RECTL rclWin;
315
316 // switch HPS to RGB mode
317 GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
318
319 // now paint
320 WinQueryWindowRect(hwnd,
321 &rclWin); // exclusive
322
323 WinFillRect(hps,
324 &rclWin, // exclusive
325 WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0));
326
327 // print question mark
328 WinDrawText(hps,
329 1,
330 "?",
331 &rclWin, // exclusive
332 WinQuerySysColor(HWND_DESKTOP, SYSCLR_WINDOWSTATICTEXT, 0),
333 WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0),
334 DT_CENTER | DT_VCENTER);
335
336 WinEndPaint(hps);
337 }
338}
339
340/*
341 *@@ fnwpXSysTray:
342 * window procedure for the Extended system tray widget class.
343 *
344 * There are a few rules which widget window procs
345 * must follow. See XCENTERWIDGETCLASS in center.h
346 * for details.
347 *
348 * Other than that, this is a regular window procedure
349 * which follows the basic rules for a PM window class.
350 */
351
352MRESULT EXPENTRY fnwpXSysTray(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
353{
354 MRESULT mrc = 0;
355 // get widget data from QWL_USER (stored there by WM_CREATE)
356 PXCENTERWIDGET pWidget = (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER);
357 // this ptr is valid after WM_CREATE
358
359 switch (msg)
360 {
361 /*
362 * WM_CREATE:
363 * as with all widgets, we receive a pointer to the
364 * XCENTERWIDGET in mp1, which was created for us.
365 *
366 * The first thing the widget MUST do on WM_CREATE
367 * is to store the XCENTERWIDGET pointer (from mp1)
368 * in the QWL_USER window word by calling:
369 *
370 * WinSetWindowPtr(hwnd, QWL_USER, mp1);
371 *
372 * We could use XCENTERWIDGET.pUser for allocating
373 * another private memory block for our own stuff,
374 * for example to be able to store fonts and colors.
375 * We ain't doing this in the minimal sample.
376 */
377
378 case WM_CREATE:
379 {
380 PSYSTRAYDATA pSysTrayData = NULL;
381
382 mrc = (MPARAM)TRUE; // being pessimistic gives more compact code
383
384 WinSetWindowPtr(hwnd, QWL_USER, mp1);
385 if ( (!(pWidget = (PXCENTERWIDGET)mp1))
386 || (!pWidget->pfnwpDefWidgetProc)
387 )
388 // shouldn't happen... stop window creation!!
389 break;
390
391 pSysTrayData = malloc(sizeof(*pSysTrayData));
392 if (pSysTrayData == NULL)
393 break;
394
395 // initialize the SYSTRAYDATA structure
396 pSysTrayData->cIconsMax = 4;
397 pSysTrayData->pIcons = malloc(sizeof(*pSysTrayData->pIcons) *
398 pSysTrayData->cIconsMax);
399 if (pSysTrayData->pIcons == NULL)
400 {
401 free(pSysTrayData);
402 break;
403 }
404 pSysTrayData->cIcons = 0;
405 pWidget->pUser = pSysTrayData;
406
407 // create the "server" window (note that we pass the XCENTERWIDGET
408 // pointer on to it)
409 HWND hwndServer =
410 WinCreateWindow(HWND_DESKTOP, WNDCLASS_WIDGET_XSYSTRAY_SERVER,
411 NULL, WS_MINIMIZED,
412 0, 0, 0, 0,
413 HWND_DESKTOP, HWND_BOTTOM,
414 0, mp1, NULL);
415 if (hwndServer == NULLHANDLE)
416 break;
417
418 mrc = FALSE; // confirm success
419 }
420 break;
421
422 /*
423 * WM_CONTROL:
424 * process notifications/queries from the XCenter.
425 */
426
427 case WM_CONTROL:
428 mrc = (MPARAM)WgtControl(pWidget, mp1, mp2);
429 break;
430
431 /*
432 * WM_PAINT:
433 * well, paint the widget.
434 */
435
436 case WM_PAINT:
437 WgtPaint(hwnd, pWidget);
438 break;
439
440 /*
441 * WM_PRESPARAMCHANGED:
442 * A well-behaved widget would intercept
443 * this and store fonts and colors.
444 */
445
446 /* case WM_PRESPARAMCHANGED:
447 break; */
448
449 /*
450 * WM_DESTROY:
451 * clean up. This _must_ be passed on to
452 * ctrDefWidgetProc.
453 */
454
455 case WM_DESTROY:
456 {
457 // free all system tray data
458 PSYSTRAYDATA pSysTrayData = (PSYSTRAYDATA)pWidget->pUser;
459 size_t i;
460 for (i = 0; i < pSysTrayData->cIcons; ++i)
461 {
462 if (pSysTrayData->pIcons[i].pszToolTip)
463 free(pSysTrayData->pIcons[i].pszToolTip);
464 }
465 free(pSysTrayData->pIcons);
466 free(pSysTrayData);
467 pWidget->pUser = NULL;
468 // We _MUST_ pass this on, or the default widget proc
469 // cannot clean up.
470 mrc = pWidget->pfnwpDefWidgetProc(hwnd, msg, mp1, mp2);
471 }
472 break;
473
474 default:
475 mrc = pWidget->pfnwpDefWidgetProc(hwnd, msg, mp1, mp2);
476
477 } // end switch(msg)
478
479 return mrc;
480}
481
482/*
483 *@@ WgtXSysTrayControl:
484 * implementation for WM_XST_CONTROL in fnwpXSysTrayServer.
485 *
486 * Serves as an entry point for all client-side requests to the Extended
487 * system tray.
488 *
489 * Note that this message is being sent from another process which is
490 * awaiting for an answer, so it must return as far as possible (by
491 * rescheduling any potentially long term operation for another cycle).
492 */
493
494static
495BOOL WgtXSysTrayControl(HWND hwnd, PXCENTERWIDGET pWidget,
496 PSYSTRAYCTLDATA pCtlData)
497{
498 BOOL brc = FALSE;
499
500 switch (pCtlData->ulCommand)
501 {
502 case SYSTRAYCMD_GETVERSION:
503 {
504 LOGF(("SYSTRAYCMD_GETVERSION\n"));
505
506 pCtlData->bAcknowledged = TRUE;
507 pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MAJOR;
508 pCtlData->u.version.ulMajor = XSYSTRAY_VERSION_MINOR;
509 pCtlData->u.version.ulRevision = XSYSTRAY_VERSION_REVISION;
510 brc = TRUE;
511 }
512 break;
513
514 default:
515 break;
516 }
517
518 return brc;
519}
520
521/*
522 *@@ fnwpXSysTrayServer:
523 * window procedure for the Extended system tray server window class.
524 *
525 * A separate "server" class is necessary because we need a CS_FRAME
526 * top-level window for DDE (which we need to support to be backward
527 * compatible with the System tray wdget from the SysTray/WPS package) and
528 * also to make ourselves discoverable for the client-side implementation
529 * of our new extended API (which queries the class of each top-level
530 * window to find the system tray server).
531 */
532
533static
534MRESULT EXPENTRY fnwpXSysTrayServer(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
535{
536 MRESULT mrc = 0;
537 // get widget data from QWL_USER_SERVER_DATA (stored there by WM_CREATE)
538 PXCENTERWIDGET pWidget =
539 (PXCENTERWIDGET)WinQueryWindowPtr(hwnd, QWL_USER_SERVER_DATA);
540 // this ptr is valid after WM_CREATE
541
542 switch (msg)
543 {
544 case WM_CREATE:
545 WinSetWindowPtr(hwnd, QWL_USER_SERVER_DATA, mp1);
546 break;
547
548 /*
549 * WM_XST_CONTROL:
550 * This is the message sent to us by the clinet-side implementation
551 * of the API to request some function. mp1 points to a
552 * SYSTRAYCTLDATA structure.
553 */
554
555 case WM_XST_CONTROL:
556 mrc = (MRESULT)WgtXSysTrayControl(hwnd, pWidget,
557 (PSYSTRAYCTLDATA)mp1);
558 break;
559
560 default:
561 mrc = WinDefWindowProc(hwnd, msg, mp1, mp2);
562 } // end switch(msg)
563
564 return mrc;
565}
566
567/* ******************************************************************
568 *
569 * Exported procedures
570 *
571 ********************************************************************/
572
573/*
574 *@@ WgtInitModule:
575 * required export with ordinal 1, which must tell
576 * the XCenter how many widgets this DLL provides,
577 * and give the XCenter an array of XCENTERWIDGETCLASS
578 * structures describing the widgets.
579 *
580 * With this call, you are given the module handle of
581 * XFLDR.DLL. For convenience, and if you have the full
582 * XWorkplace source code, you could resolve imports
583 * for some useful functions which are exported thru
584 * src\shared\xwp.def. We don't do this here.
585 *
586 * This function must also register the PM window classes
587 * which are specified in the XCENTERWIDGETCLASS array
588 * entries. For this, you are given a HAB which you
589 * should pass to WinRegisterClass. For the window
590 * class style (4th param to WinRegisterClass),
591 * you should specify
592 *
593 + CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT
594 *
595 * Your widget window _will_ be resized by the XCenter,
596 * even if you're not planning it to be.
597 *
598 * This function only gets called _once_ when the widget
599 * DLL has been successfully loaded by the XCenter. If
600 * there are several instances of a widget running (in
601 * the same or in several XCenters), this function does
602 * not get called again. However, since the XCenter unloads
603 * the widget DLLs again if they are no longer referenced
604 * by any XCenter, this might get called again when the
605 * DLL is re-loaded.
606 *
607 * There will ever be only one load occurence of the DLL.
608 * The XCenter manages sharing the DLL between several
609 * XCenters. As a result, it doesn't matter if the DLL
610 * has INITINSTANCE etc. set or not.
611 *
612 * If this returns 0, this is considered an error, and the
613 * DLL will be unloaded again immediately.
614 *
615 * If this returns any value > 0, *ppaClasses must be
616 * set to a static array (best placed in the DLL's
617 * global data) of XCENTERWIDGETCLASS structures,
618 * which must have as many entries as the return value.
619 */
620
621ULONG EXPENTRY WgtInitModule(HAB hab, // XCenter's anchor block
622 HMODULE hmodPlugin, // module handle of the widget DLL
623 HMODULE hmodXFLDR, // XFLDR.DLL module handle
624 PCXCENTERWIDGETCLASS *ppaClasses,
625 PSZ pszErrorMsg) // if 0 is returned, 500 bytes of error msg
626{
627 ULONG ulrc = 0;
628 CLASSINFO ClassInfo;
629
630 LOGF(("hmodPlugin %x\n", hmodPlugin));
631
632 do
633 {
634 // register our PM window class
635 if (!WinRegisterClass(hab,
636 WNDCLASS_WIDGET_XSYSTRAY,
637 fnwpXSysTray,
638 CS_PARENTCLIP | CS_SIZEREDRAW | CS_SYNCPAINT,
639 sizeof(PVOID))
640 // extra memory to reserve for QWL_USER
641 )
642 {
643 LOG(("WinRegisterClass(%s) failed with %lX",
644 WNDCLASS_WIDGET_XSYSTRAY, WinGetLastError(hab)));
645 break;
646 }
647
648 // get the window data size for the WC_FRAME class (any window class
649 // that specifies CS_FRAME must have at least this number, otherise
650 // WinRegisterClass returns 0x1003
651 if (!WinQueryClassInfo(hab, (PSZ)WC_FRAME, &ClassInfo))
652 break;
653 QWL_USER_SERVER_DATA = ClassInfo.cbWindowData;
654
655 if (!WinRegisterClass(hab,
656 WNDCLASS_WIDGET_XSYSTRAY_SERVER,
657 fnwpXSysTrayServer,
658 CS_FRAME,
659 QWL_USER_SERVER_DATA + sizeof(PVOID))
660 // extra memory to reserve for QWL_USER
661 )
662 {
663 // error registering class: report error then
664 snprintf(pszErrorMsg, 500, "WinRegisterClass(%s) failed with %lX",
665 WNDCLASS_WIDGET_XSYSTRAY_SERVER, WinGetLastError(hab));
666 break;
667 }
668
669 // no error:
670 // return widget classes array
671 *ppaClasses = G_WidgetClasses;
672
673 // return no. of classes in this DLL (one here):
674 ulrc = sizeof(G_WidgetClasses) / sizeof(G_WidgetClasses[0]);
675 }
676 while (0);
677
678 LOGF(("pszErrorMsg %s\n", pszErrorMsg));
679 LOGF(("ulrc %d\n", ulrc));
680
681 return ulrc;
682}
683
684/*
685 *@@ WgtUnInitModule:
686 * optional export with ordinal 2, which can clean
687 * up global widget class data.
688 *
689 * This gets called by the XCenter right before
690 * a widget DLL gets unloaded. Note that this
691 * gets called even if the "init module" export
692 * returned 0 (meaning an error) and the DLL
693 * gets unloaded right away.
694 */
695
696VOID EXPENTRY WgtUnInitModule(VOID)
697{
698}
699
700/*
701 *@@ WgtQueryVersion:
702 * this new export with ordinal 3 can return the
703 * XWorkplace version number which is required
704 * for this widget to run. For example, if this
705 * returns 0.9.10, this widget will not run on
706 * earlier XWorkplace versions.
707 *
708 * NOTE: This export was mainly added because the
709 * prototype for the "Init" export was changed
710 * with V0.9.9. If this returns 0.9.9, it is
711 * assumed that the INIT export understands
712 * the new FNWGTINITMODULE_099 format (see center.h).
713 */
714
715VOID EXPENTRY WgtQueryVersion(PULONG pulMajor,
716 PULONG pulMinor,
717 PULONG pulRevision)
718{
719 *pulMajor = 0;
720 *pulMinor = 9;
721 *pulRevision = 9;
722}
723
Note: See TracBrowser for help on using the repository browser.