2 * $XConsortium: Mailbox.c,v 1.35 89/10/09 16:51:44 jim Exp $
4 * Copyright 1988 Massachusetts Institute of Technology
6 * Permission to use, copy, modify, and distribute this software and its
7 * documentation for any purpose and without fee is hereby granted, provided
8 * that the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of M.I.T. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. M.I.T. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * Author: Jim Fulton, MIT X Consortium
18 * I recommend that you use the new mailfull and mailempty bitmaps instead of
21 * XBiff*fullPixmap: mailfull
22 * XBiff*emptyPixmap: mailempty
25 #include <stdio.h> /* for printing error messages */
26 #include <pwd.h> /* for getting username */
28 #include <X11/cursorfont.h> /* for cursor constants */
29 #include <X11/StringDefs.h> /* for useful atom names */
30 #include <X11/Intrinsic.h> /* for XtTimerCallbackProc */
31 #include <X11/IntrinsicP.h> /* for toolkit stuff */
32 #include <sys/stat.h> /* for stat() ** needs types.h ***/
34 #include <X11/bitmaps/mailfull> /* for flag up (mail present) bits */
35 #include <X11/bitmaps/mailempty> /* for flag down (mail not here) */
37 #include <./Xaw3_1XawInit.h>
38 #include <./Xaw3_1MailboxP.h> /* for implementation mailbox stuff */
40 #include <X11/Xmu/Converters.h> /* for XmuCvtStringToBitmap */
43 #include <X11/extensions/shape.h>
47 * The default user interface is to have the mailbox turn itself off whenever
48 * the user presses a button in it. Expert users might want to make this
49 * happen on EnterWindow. It might be nice to provide support for some sort of
50 * exit callback so that you can do things like press q to quit.
53 static char defaultTranslations
[] =
54 "<ButtonPress>: unset()";
56 static void Check(), Set(), Unset();
58 static XtActionsRec actionsList
[] = {
65 /* Initialization of defaults */
67 #define offset(field) XtOffset(MailboxWidget,mailbox.field)
68 #define goffset(field) XtOffset(Widget,core.field)
70 static Dimension defDim
= 48;
71 static Pixmap nopix
= None
;
73 static XtResource resources
[] = {
74 { XtNwidth
, XtCWidth
, XtRDimension
, sizeof (Dimension
),
75 goffset (width
), XtRDimension
, (caddr_t
)&defDim
},
76 { XtNheight
, XtCHeight
, XtRDimension
, sizeof (Dimension
),
77 goffset (height
), XtRDimension
, (caddr_t
)&defDim
},
78 { XtNupdate
, XtCInterval
, XtRInt
, sizeof (int),
79 offset (update
), XtRString
, "30" },
80 { XtNforeground
, XtCForeground
, XtRPixel
, sizeof (Pixel
),
81 offset (foreground_pixel
), XtRString
, "black" },
82 { XtNbackground
, XtCBackground
, XtRPixel
, sizeof (Pixel
),
83 goffset (background_pixel
), XtRString
, "white" },
84 { XtNreverseVideo
, XtCReverseVideo
, XtRBoolean
, sizeof (Boolean
),
85 offset (reverseVideo
), XtRString
, "FALSE" },
86 { XtNfile
, XtCFile
, XtRString
, sizeof (String
),
87 offset (filename
), XtRString
, NULL
},
88 { XtNcheckCommand
, XtCCheckCommand
, XtRString
, sizeof(char*),
89 offset (check_command
), XtRString
, NULL
},
90 { XtNvolume
, XtCVolume
, XtRInt
, sizeof(int),
91 offset (volume
), XtRString
, "33"},
92 { XtNonceOnly
, XtCBoolean
, XtRBoolean
, sizeof(Boolean
),
93 offset (once_only
), XtRImmediate
, (caddr_t
)False
},
94 { XtNfullPixmap
, XtCPixmap
, XtRBitmap
, sizeof(Pixmap
),
95 offset (full
.bitmap
), XtRString
, "flagup" },
96 { XtNfullPixmapMask
, XtCPixmapMask
, XtRBitmap
, sizeof(Pixmap
),
97 offset (full
.mask
), XtRBitmap
, (caddr_t
) &nopix
},
98 { XtNemptyPixmap
, XtCPixmap
, XtRBitmap
, sizeof(Pixmap
),
99 offset (empty
.bitmap
), XtRString
, "flagdown" },
100 { XtNemptyPixmapMask
, XtCPixmapMask
, XtRBitmap
, sizeof(Pixmap
),
101 offset (empty
.mask
), XtRBitmap
, (caddr_t
) &nopix
},
102 { XtNflip
, XtCFlip
, XtRBoolean
, sizeof(Boolean
),
103 offset (flipit
), XtRString
, "true" },
105 { XtNshapeWindow
, XtCShapeWindow
, XtRBoolean
, sizeof(Boolean
),
106 offset (shapeit
), XtRString
, "false" },
113 static void GetMailFile(), CloseDown();
114 static void check_mailbox(), redraw_mailbox(), beep();
115 static void ClassInitialize(), Initialize(), Realize(), Destroy(), Redisplay();
116 static Boolean
SetValues();
118 MailboxClassRec mailboxClassRec
= {
120 /* superclass */ &widgetClassRec
,
121 /* class_name */ "Mailbox",
122 /* widget_size */ sizeof(MailboxRec
),
123 /* class_initialize */ ClassInitialize
,
124 /* class_part_initialize */ NULL
,
125 /* class_inited */ FALSE
,
126 /* initialize */ Initialize
,
127 /* initialize_hook */ NULL
,
128 /* realize */ Realize
,
129 /* actions */ actionsList
,
130 /* num_actions */ XtNumber(actionsList
),
131 /* resources */ resources
,
132 /* resource_count */ XtNumber(resources
),
133 /* xrm_class */ NULL
,
134 /* compress_motion */ TRUE
,
135 /* compress_exposure */ TRUE
,
136 /* compress_enterleave */ TRUE
,
137 /* visible_interest */ FALSE
,
138 /* destroy */ Destroy
,
140 /* expose */ Redisplay
,
141 /* set_values */ SetValues
,
142 /* set_values_hook */ NULL
,
143 /* set_values_almost */ XtInheritSetValuesAlmost
,
144 /* get_values_hook */ NULL
,
145 /* accept_focus */ NULL
,
146 /* version */ XtVersion
,
147 /* callback_private */ NULL
,
148 /* tm_table */ defaultTranslations
,
149 /* query_geometry */ XtInheritQueryGeometry
,
150 /* display_accelerator */ XtInheritDisplayAccelerator
,
155 WidgetClass mailboxWidgetClass
= (WidgetClass
) &mailboxClassRec
;
159 * widget initialization
162 static void ClassInitialize ()
164 static XtConvertArgRec screenConvertArg
[] = {
165 { XtWidgetBaseOffset
, (caddr_t
) XtOffset(Widget
, core
.screen
), sizeof(Screen
*) }
168 XawInitializeWidgetSet();
169 XtAddConverter (XtRString
, XtRBitmap
, XmuCvtStringToBitmap
,
170 screenConvertArg
, XtNumber(screenConvertArg
));
174 static GC
get_mailbox_gc (w
)
180 valuemask
= GCForeground
| GCBackground
| GCFunction
| GCGraphicsExposures
;
181 xgcv
.foreground
= w
->mailbox
.foreground_pixel
;
182 xgcv
.background
= w
->core
.background_pixel
;
183 xgcv
.function
= GXcopy
;
184 xgcv
.graphics_exposures
= False
; /* this is Bool, not Boolean */
185 return (XtGetGC ((Widget
) w
, valuemask
, &xgcv
));
190 static void Initialize (request
, new)
193 MailboxWidget w
= (MailboxWidget
) new;
195 int shape_event_base
, shape_error_base
;
198 if (!w
->mailbox
.filename
) GetMailFile (w
);
200 if (w
->core
.width
<= 0) w
->core
.width
= 1;
201 if (w
->core
.height
<= 0) w
->core
.height
= 1;
203 if (w
->mailbox
.reverseVideo
) {
206 tmp
= w
->mailbox
.foreground_pixel
;
207 w
->mailbox
.foreground_pixel
= w
->core
.background_pixel
;
208 w
->core
.background_pixel
= tmp
;
212 if (w
->mailbox
.shapeit
&& !XShapeQueryExtension (XtDisplay (w
),
215 w
->mailbox
.shapeit
= False
;
216 w
->mailbox
.shape_cache
.mask
= None
;
219 w
->mailbox
.gc
= get_mailbox_gc (w
);
220 w
->mailbox
.interval_id
= (XtIntervalId
) 0;
221 w
->mailbox
.full
.pixmap
= None
;
222 w
->mailbox
.empty
.pixmap
= None
;
233 * pretend there is new mail; put widget in flagup state
237 static void Set (gw
, event
, params
, nparams
)
243 MailboxWidget w
= (MailboxWidget
) gw
;
245 w
->mailbox
.last_size
= -1;
247 check_mailbox (w
, TRUE
, FALSE
); /* redraw, no reset */
254 * ack the existing mail; put widget in flagdown state
258 static void Unset (gw
, event
, params
, nparams
)
264 MailboxWidget w
= (MailboxWidget
) gw
;
266 check_mailbox (w
, TRUE
, TRUE
); /* redraw, reset */
273 * look to see if there is new mail; if so, Set, else Unset
277 static void Check (gw
, event
, params
, nparams
)
283 MailboxWidget w
= (MailboxWidget
) gw
;
285 check_mailbox (w
, TRUE
, FALSE
); /* redraw, no reset */
292 static XtTimerCallbackProc
clock_tic (client_data
, id
)
296 MailboxWidget w
= (MailboxWidget
) client_data
;
298 check_mailbox (w
, FALSE
, FALSE
); /* no redraw, no reset */
301 * and reset the timer
304 w
->mailbox
.interval_id
= XtAddTimeOut (w
->mailbox
.update
* 1000,
305 (XtTimerCallbackProc
)clock_tic
,
311 static Pixmap
make_pixmap (dpy
, w
, bitmap
, depth
, flip
, widthp
, heightp
)
317 int *widthp
, *heightp
;
321 unsigned int width
, height
, bw
, dep
;
322 unsigned long fore
, back
;
324 if (!XGetGeometry (dpy
, bitmap
, &root
, &x
, &y
, &width
, &height
, &bw
, &dep
))
327 *widthp
= (int) width
;
328 *heightp
= (int) height
;
330 fore
= w
->core
.background_pixel
;
331 back
= w
->mailbox
.foreground_pixel
;
333 fore
= w
->mailbox
.foreground_pixel
;
334 back
= w
->core
.background_pixel
;
336 return XmuCreatePixmapFromBitmap (dpy
, w
->core
.window
, bitmap
,
337 width
, height
, depth
, fore
, back
);
340 static void Realize (gw
, valuemaskp
, attr
)
342 XtValueMask
*valuemaskp
;
343 XSetWindowAttributes
*attr
;
345 MailboxWidget w
= (MailboxWidget
) gw
;
346 register Display
*dpy
= XtDisplay (w
);
347 int depth
= w
->core
.depth
;
349 *valuemaskp
|= (CWBitGravity
| CWCursor
);
350 attr
->bit_gravity
= ForgetGravity
;
351 attr
->cursor
= XCreateFontCursor (dpy
, XC_top_left_arrow
);
353 XtCreateWindow (gw
, InputOutput
, (Visual
*) CopyFromParent
,
357 * build up the pixmaps that we'll put into the image
359 if (w
->mailbox
.full
.bitmap
== None
) {
360 w
->mailbox
.full
.bitmap
=
361 XCreateBitmapFromData (dpy
, w
->core
.window
,
362 (const char *)mailfull_bits
,
363 mailfull_width
, mailfull_height
);
365 if (w
->mailbox
.empty
.bitmap
== None
) {
366 w
->mailbox
.empty
.bitmap
=
367 XCreateBitmapFromData (dpy
, w
->core
.window
,
368 (const char *)mailempty_bits
,
369 mailempty_width
, mailempty_height
);
372 w
->mailbox
.empty
.pixmap
= make_pixmap (dpy
, w
, w
->mailbox
.empty
.bitmap
,
374 &w
->mailbox
.empty
.width
,
375 &w
->mailbox
.empty
.height
);
376 w
->mailbox
.full
.pixmap
= make_pixmap (dpy
, w
, w
->mailbox
.full
.bitmap
,
377 depth
, w
->mailbox
.flipit
,
378 &w
->mailbox
.full
.width
,
379 &w
->mailbox
.full
.height
);
382 if (w
->mailbox
.empty
.mask
== None
&& w
->mailbox
.full
.mask
== None
)
383 w
->mailbox
.shapeit
= False
;
386 w
->mailbox
.interval_id
= XtAddTimeOut (w
->mailbox
.update
* 1000,
387 (XtTimerCallbackProc
)clock_tic
,
391 w
->mailbox
.shape_cache
.mask
= None
;
398 static void Destroy (gw
)
401 MailboxWidget w
= (MailboxWidget
) gw
;
402 Display
*dpy
= XtDisplay (gw
);
404 XtFree (w
->mailbox
.filename
);
405 if (w
->mailbox
.interval_id
) XtRemoveTimeOut (w
->mailbox
.interval_id
);
406 XtDestroyGC (w
->mailbox
.gc
);
407 #define freepix(p) if (p) XFreePixmap (dpy, p)
408 freepix (w
->mailbox
.full
.bitmap
); /* until cvter does ref cnt */
409 freepix (w
->mailbox
.full
.mask
); /* until cvter does ref cnt */
410 freepix (w
->mailbox
.full
.pixmap
);
411 freepix (w
->mailbox
.empty
.bitmap
); /* until cvter does ref cnt */
412 freepix (w
->mailbox
.empty
.mask
); /* until cvter does ref cnt */
413 freepix (w
->mailbox
.empty
.pixmap
);
415 freepix (w
->mailbox
.shape_cache
.mask
);
422 static void Redisplay (gw
)
425 MailboxWidget w
= (MailboxWidget
) gw
;
427 check_mailbox (w
, TRUE
, FALSE
);
431 static void check_mailbox (w
, force_redraw
, reset
)
433 Boolean force_redraw
, reset
;
435 long mailboxsize
= 0;
437 if (w
->mailbox
.check_command
!= NULL
) {
438 switch (system(w
->mailbox
.check_command
)) {
440 mailboxsize
= w
->mailbox
.last_size
+ 1;
442 /* case 1 is no change */
445 /* treat everything else as no change */
451 if (stat (w
->mailbox
.filename
, &st
) == 0) {
452 mailboxsize
= st
.st_size
;
457 * Now check for changes. If reset is set then we want to pretent that
458 * there is no mail. If the mailbox is empty then we want to turn off
459 * the flag. Otherwise if the mailbox has changed size then we want to
463 * o forced reset by user DOWN
464 * o no mailbox or empty (zero-sized) mailbox DOWN
465 * o same size as last time no change
466 * o bigger than last time UP
467 * o smaller than last time but non-zero UP
469 * The last two cases can be expressed as different from last
473 if (reset
) { /* forced reset */
474 w
->mailbox
.flag_up
= FALSE
;
476 } else if (mailboxsize
== 0) { /* no mailbox or empty */
477 w
->mailbox
.flag_up
= FALSE
;
478 if (w
->mailbox
.last_size
> 0) force_redraw
= TRUE
; /* if change */
479 } else if (mailboxsize
!= w
->mailbox
.last_size
) { /* different size */
480 if (!w
->mailbox
.once_only
|| !w
->mailbox
.flag_up
)
482 w
->mailbox
.flag_up
= TRUE
;
486 w
->mailbox
.last_size
= mailboxsize
;
487 if (force_redraw
) redraw_mailbox (w
);
492 * get user name for building mailbox
495 static void GetMailFile (w
)
501 username
= getlogin ();
503 struct passwd
*pw
= getpwuid (getuid ());
506 fprintf (stderr
, "%s: unable to find a username for you.\n",
510 username
= pw
->pw_name
;
512 w
->mailbox
.filename
= (String
) XtMalloc (strlen (MAILBOX_DIRECTORY
) + 1 +
513 strlen (username
) + 1);
514 strcpy (w
->mailbox
.filename
, MAILBOX_DIRECTORY
);
515 strcat (w
->mailbox
.filename
, "/");
516 strcat (w
->mailbox
.filename
, username
);
520 static void CloseDown (w
, status
)
524 Display
*dpy
= XtDisplay (w
);
526 XtDestroyWidget ((Widget
)w
);
533 static Boolean
SetValues (gcurrent
, grequest
, gnew
)
534 Widget gcurrent
, grequest
, gnew
;
536 MailboxWidget current
= (MailboxWidget
) gcurrent
;
537 MailboxWidget
new = (MailboxWidget
) gnew
;
538 Boolean redisplay
= FALSE
;
540 if (current
->mailbox
.update
!= new->mailbox
.update
) {
541 if (current
->mailbox
.interval_id
)
542 XtRemoveTimeOut (current
->mailbox
.interval_id
);
543 new->mailbox
.interval_id
= XtAddTimeOut (new->mailbox
.update
* 1000,
544 (XtTimerCallbackProc
)clock_tic
,
548 if (current
->mailbox
.foreground_pixel
!= new->mailbox
.foreground_pixel
||
549 current
->core
.background_pixel
!= new->core
.background_pixel
) {
550 XtDestroyGC (current
->mailbox
.gc
);
551 new->mailbox
.gc
= get_mailbox_gc (new);
563 static void redraw_mailbox (w
)
566 register Display
*dpy
= XtDisplay (w
);
567 register Window win
= XtWindow (w
);
569 GC gc
= w
->mailbox
.gc
;
570 Pixel back
= w
->core
.background_pixel
;
573 /* center the picture in the window */
575 if (w
->mailbox
.flag_up
) { /* paint the "up" position */
576 im
= &w
->mailbox
.full
;
577 if (w
->mailbox
.flipit
) back
= w
->mailbox
.foreground_pixel
;
578 } else { /* paint the "down" position */
579 im
= &w
->mailbox
.empty
;
581 x
= (((int)w
->core
.width
) - im
->width
) / 2;
582 y
= (((int)w
->core
.height
) - im
->height
) / 2;
584 XSetWindowBackground (dpy
, win
, back
);
585 XClearWindow (dpy
, win
);
586 XCopyArea (dpy
, im
->pixmap
, win
, gc
, 0, 0, im
->width
, im
->height
, x
, y
);
590 * XXX - temporary hack; walk up widget tree to find top most parent (which
591 * will be a shell) and mash it to have our shape. This will be replaced
592 * by a special shell widget.
594 if (w
->mailbox
.shapeit
) {
597 for (parent
= (Widget
) w
; XtParent(parent
);
598 parent
= XtParent(parent
)) {
599 x
+= parent
->core
.x
+ parent
->core
.border_width
;
600 y
+= parent
->core
.y
+ parent
->core
.border_width
;
603 if (im
->mask
!= w
->mailbox
.shape_cache
.mask
||
604 x
!= w
->mailbox
.shape_cache
.x
|| y
!= w
->mailbox
.shape_cache
.y
) {
605 XShapeCombineMask (XtDisplay(parent
), XtWindow(parent
),
606 ShapeBounding
, x
, y
, im
->mask
, ShapeSet
);
607 w
->mailbox
.shape_cache
.mask
= im
->mask
;
608 w
->mailbox
.shape_cache
.x
= x
;
609 w
->mailbox
.shape_cache
.y
= y
;
621 XBell (XtDisplay (w
), w
->mailbox
.volume
);