1 /*
2  * Copyright (c) 1994-96  Dimitrios P. Bouras and William K. W. Cheung
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17  * X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  *
21  * Except as contained in this notice, the name of the X Consortium shall not be
22  * used in advertising or otherwise to promote the sale, use or other dealings
23  * in this Software without prior written authorization from the X Consortium.
24  *
25  * Derived from the MIT X11R5 xbiff, written by Jim Fulton, which is
26  * copyrighted (c) 1988 X Consortium.
27  *
28  * Mailbox XPM additions-modifications:  Dimitrios P. Bouras
29  * Audio support and XPM icon animation: William K. W. Cheung
30  */
31 
32 #include <X11/IntrinsicP.h>		/* for toolkit stuff */
33 #include <X11/StringDefs.h>		/* for useful atom names */
34 #include <X11/cursorfont.h>		/* for cursor constants */
35 #include <X11/Xosdefs.h>		/* for X_NOT_POSIX def */
36 #include <sys/stat.h>			/* for stat() ** needs types.h ***/
37 #include <sys/signal.h>			/* for signal() */
38 #include <stdio.h>				/* for printing error messages */
39 #include <pwd.h>				/* for getting username */
40 #include <stdlib.h>				/* for getenv() */
41 #include <string.h>
42 
43 #ifndef NO_AUDIO
44 #ifdef NCD_AUDIO
45 #include <audio/audiolib.h>
46 #include <audio/soundlib.h>
47 
48 static AuServer *aud;			/* Audio server handler */
49 #elif defined(RPLAY_AUDIO)
50 #include <rplay.h>
51 #endif /* ifdef NCD_AUDIO */
52 #endif /* ifndef NO_AUDIO */
53 
54 #include <sys/file.h>
55 #include <sys/fcntl.h>
56 #include <sys/ioctl.h>
57 
58 #ifndef NO_AUDIO
59 #ifdef SUN_AUDIO
60 
61 /* Adapted from <multimedia/audio_filehdr.h> */
62 
63 typedef unsigned 	u_32;	/* we assume sizeof(unsigned) = 4 */
64 
65 typedef struct {
66 	u_32		magic;		/* magic number */
67 	u_32		hdr_size;	/* size of this header */
68 	u_32		data_size;	/* length of data (optional) */
69 	u_32		encoding;	/* data encoding format */
70 	u_32		sample_rate;	/* samples per second */
71 	u_32		channels;	/* number of interleaved channels */
72 } Audio_filehdr;
73 
74 #if defined(linux) || defined(__FreeBSD__)
75 #ifdef linux
76 #include <linux/soundcard.h>
77 #else
78 #include <sys/soundcard.h>
79 #endif /* ifdef linux */
80 
81 #define DEV_MIXER		"/dev/mixer"
82 #define MAX_VOLUME		100
83 #define MIN_VOLUME		1
84 #define RIGHT			0x01
85 #define LEFT			0x02
86 
87 typedef struct stereovolume
88 {
89 	unsigned char left;
90 	unsigned char right;
91 	unsigned char pad[2];
92 } StereoVolume;
93 
setvolume(int which,unsigned char setting,StereoVolume * volptr)94 void setvolume(int which, unsigned char setting, StereoVolume *volptr)
95 {
96 	if ( setting < MIN_VOLUME )
97 		setting=MIN_VOLUME;
98 	if ( setting > MAX_VOLUME )
99 		setting=MAX_VOLUME;
100 
101 	if ( which&RIGHT )
102 		volptr->right=setting;
103 	if ( which&LEFT )
104 		volptr->left=setting;
105 }
106 #else
107 #include <sun/audioio.h>
108 #endif /* if defined(linux) */
109 #endif /* ifdef SUN_AUDIO */
110 #endif /* ifndef NO_AUDIO */
111 
112 #ifndef X_NOT_POSIX
113 #ifdef _POSIX_SOURCE
114 # include <sys/wait.h>
115 #else
116 #define _POSIX_SOURCE
117 # include <sys/wait.h>
118 #undef _POSIX_SOURCE
119 #endif
120 # define waitCode(w)	WEXITSTATUS(w)
121 # define waitSig(w)	WIFSIGNALED(w)
122 typedef int		waitType;
123 # define INTWAITTYPE
124 #else /* ! X_NOT_POSIX */
125 #ifdef SYSV
126 # define waitCode(w)	(((w) >> 8) & 0x7f)
127 # define waitSig(w)	((w) & 0xff)
128 typedef int		waitType;
129 # define INTWAITTYPE
130 #else
131 # include	<sys/wait.h>
132 # define waitCode(w)	((w).w_T.w_Retcode)
133 # define waitSig(w)	((w).w_T.w_Termsig)
134 typedef union wait	waitType;
135 #endif /* SYSV else */
136 #endif /* ! X_NOT_POSIX else */
137 
138 #include "xmail.xpm"		/* for flag up (mail present) bits */
139 #include "xnomail.xpm"		/* for flag down (mail not here) */
140 
141 #ifndef min
142 #define min(x,y)	((x) < (y)? (x): (y))
143 #endif
144 
145 #include <X11/Xaw/XawInit.h>
146 #include "MailboxP.h"			/* for implementation mailbox stuff */
147 #include <X11/Xmu/Drawing.h>
148 #include <X11/extensions/shape.h>
149 
150 /*
151  * The default user interface is to have the mailbox turn itself off whenever
152  * the user presses a button in it.  Expert users might want to make this
153  * happen on EnterWindow.  It might be nice to provide support for some sort of
154  * exit callback so that you can do things like press q to quit.
155  */
156 
157 static char defaultTranslations[] =
158   "<ButtonPress>(2):  unset()\n\
159    <ButtonPress>:     ack()";
160 
161 static void Check(), Set(), Unset(), Ack();
162 
163 static XtActionsRec actionsList[] = {
164     { "check",	Check },
165     { "unset",	Unset },
166     { "set",	Set },
167     { "ack",	Ack },
168 };
169 
170 /*
171  * Storage for the XPM images for the two mailbox states.
172  */
173 static XpmImage mail_xpmimg[MAX_ANIM_IMAGE];
174 static XpmImage nomail_xpmimg;
175 
176 /* Initialization of defaults */
177 
178 #define offset(field) XtOffsetOf(Mailbox_XPM_Rec, mailbox.field)
179 #define goffset(field) XtOffsetOf(WidgetRec, core.field)
180 
181 static XtResource resources[] = {
182     { XtNupdate, XtCInterval, XtRInt, sizeof (int),
183 	offset (update), XtRImmediate, (XtPointer)30 },
184     { XtNfile, XtCFile, XtRString, sizeof (String),
185 	offset (filename), XtRString, NULL },
186     { XtNcheckCommand, XtCCheckCommand, XtRString, sizeof(char*),
187 	offset (check_command), XtRString, NULL },
188     { XtNvolume, XtCVolume, XtRInt, sizeof(int),
189 	offset (volume), XtRImmediate, (XtPointer)33 },
190     { XtNonceOnly, XtCBoolean, XtRBoolean, sizeof(Boolean),
191 	offset (once_only), XtRImmediate, (XtPointer)False },
192     { Nmailtool, CMailTool, XtRString, sizeof (String),
193 	offset (mail_tool), XtRString, NULL },
194     { XtNmailAnimUpdate, XtCMailAnimUpdate, XtRInt, sizeof (int),
195 	offset (mail_animupdate), XtRImmediate, (XtPointer)1000 },
196     { XtNmailAnimOnce, XtCMailAnimOnce, XtRBoolean, sizeof (Boolean),
197 	offset (mail_animonce), XtRImmediate, (XtPointer)False },
198     { XtNmailNumOfXpmFile, XtCMailNumOfXpmFile, XtRInt, sizeof (int),
199 	offset (mail_numofxpmfile), XtRImmediate, (XtPointer)1 },
200     { NmailXpmFile, CMailXpmFile, XtRString, sizeof (String),
201 	offset (mail_xpmfile), XtRString, NULL },
202     { NnomailXpmFile, CNomailXpmFile, XtRString, sizeof (String),
203 	offset (nomail_xpmfile), XtRString, NULL },
204     { NmailSndFile, CMailSndFile, XtRString, sizeof (String),
205 	offset (mail_sndfile), XtRString, NULL },
206     { NmailSndComm, CMailSndComm, XtRString, sizeof (String),
207 	offset (mail_sndcomm), XtRString, NULL },
208 };
209 
210 #undef offset
211 
212 static void GetMailFile(), CloseDown(), ReadIconFile(), DefaultXpmImage();
213 static void check_mailbox(), redraw_mailbox(), beep();
214 static void Initialize(), Realize(), Destroy(), Redisplay();
215 static Boolean SetValues();
216 
217 Mailbox_XPM_ClassRec mailboxClassRec = {
218     { /* core fields */
219     /* superclass				*/	(WidgetClass) &simpleClassRec,
220     /* class_name				*/	"Mailbox",
221     /* widget_size				*/	sizeof(Mailbox_XPM_Rec),
222     /* class_initialize			*/	XawInitializeWidgetSet,
223     /* class_part_initialize	*/	NULL,
224     /* class_inited				*/	FALSE,
225     /* initialize				*/	Initialize,
226     /* initialize_hook			*/	NULL,
227     /* realize					*/	Realize,
228     /* actions					*/	actionsList,
229     /* num_actions				*/	XtNumber(actionsList),
230     /* resources				*/	resources,
231     /* resource_count			*/	XtNumber(resources),
232     /* xrm_class				*/	NULLQUARK,
233     /* compress_motion			*/	TRUE,
234     /* compress_exposure		*/	TRUE,
235     /* compress_enterleave		*/	TRUE,
236     /* visible_interest			*/	FALSE,
237     /* destroy					*/	Destroy,
238     /* resize					*/	NULL,
239     /* expose					*/	Redisplay,
240     /* set_values				*/	SetValues,
241     /* set_values_hook			*/	NULL,
242     /* set_values_almost		*/	XtInheritSetValuesAlmost,
243     /* get_values_hook			*/	NULL,
244     /* accept_focus				*/	NULL,
245     /* version					*/	XtVersion,
246     /* callback_private			*/	NULL,
247     /* tm_table					*/	defaultTranslations,
248     /* query_geometry			*/	XtInheritQueryGeometry,
249     /* display_accelerator		*/	XtInheritDisplayAccelerator,
250     /* extension				*/	NULL
251     },
252     { /* simple fields */
253     /* change_sensitive         */	XtInheritChangeSensitive
254     },
255     { /* mailbox fields */
256     /* ignore                   */	0
257     }
258 };
259 
260 WidgetClass mailboxWidgetClass = (WidgetClass) &mailboxClassRec;
261 
262 
263 /*
264  * widget initialization
265  */
266 
get_mailbox_gc(w)267 static GC get_mailbox_gc (w)
268     MailboxWidget w;
269 {
270     XtGCMask valuemask;
271     XGCValues xgcv;
272 
273     valuemask = GCFunction | GCGraphicsExposures;
274     xgcv.function = GXcopy;
275     xgcv.graphics_exposures = False;	/* this is Bool, not Boolean */
276     return (XtGetGC ((Widget) w, valuemask, &xgcv));
277 }
278 
zombiekiller()279 int zombiekiller()
280 {
281 	int	status;
282 
283 	while (wait3(&status, WNOHANG, 0) >= 0);
284 #ifdef linux
285 	signal(SIGCHLD, (SignalHandler)zombiekiller);
286 #endif
287 }
288 
289 /* ARGSUSED */
Initialize(request,new)290 static void Initialize (request, new)
291     Widget request, new;
292 {
293     MailboxWidget w = (MailboxWidget) new;
294     int shape_event_base, shape_error_base;
295 
296 #ifdef SYSV
297 	signal(SIGCLD, zombiekiller);
298 #elif defined(linux)
299 	signal(SIGCHLD, (SignalHandler)zombiekiller);
300 #else
301 	signal(SIGCHLD, zombiekiller);
302 #endif
303 
304     if ( !XShapeQueryExtension (XtDisplay (w), &shape_event_base,
305                                                &shape_error_base)) {
306 	fprintf (stderr, "%s:  shape extensions not supported!\n",
307 		 "Mailbox widget");
308 	CloseDown (w, 1);
309     }
310 
311     w->mailbox.shape_cache.mask = None;
312     w->mailbox.gc = get_mailbox_gc (w);
313     w->mailbox.interval_id = (XtIntervalId) 0;
314     w->mailbox.anim_int_id = (XtIntervalId) -1;
315     w->mailbox.first_trig = 1;
316     w->mailbox.flag_up = FALSE;
317     w->mailbox.last_size = 0;
318 	w->mailbox.anim_id = 0;
319 
320 	if ((w->mailbox.mail_numofxpmfile < 1)
321 		|| (w->mailbox.mail_numofxpmfile > MAX_ANIM_IMAGE)) {
322 		fprintf (stderr,
323 				 "%s:  Number of Xpm Images must be between 1 and 8 !\n",
324 				 "Mailbox widget");
325 		CloseDown (w, 1);
326 	}
327 
328     if (!w->mailbox.filename) GetMailFile (w);
329 
330     /*
331      * read the XPM files from the resources, if any, and create
332      * XpmImages, or create XpmImages from default image data
333      */
334 
335     w->mailbox.full[0].xpmimg = None;
336     if (w->mailbox.mail_xpmfile) ReadIconFile(w, True);
337     if (w->mailbox.full[0].xpmimg == None) DefaultXpmImage(w, True);
338 
339     w->mailbox.empty.xpmimg = None;
340     if (w->mailbox.nomail_xpmfile) ReadIconFile(w, False);
341     if (w->mailbox.empty.xpmimg == None) DefaultXpmImage(w, False);
342 
343 #define _MAX(x,y) ((x>y)?x:y)
344     w->core.width = _MAX( w->mailbox.full[0].width, w->mailbox.empty.width);
345     w->core.height = _MAX( w->mailbox.full[0].height, w->mailbox.empty.height);
346 #undef _MAX
347 
348     return;
349 }
350 
351 
352 /*
353  * action procedures
354  */
355 
356 /*
357  * pretend there is new mail; put widget in flagup state
358  */
359 
360 /* ARGSUSED */
Set(gw,event,params,nparams)361 static void Set (gw, event, params, nparams)
362     Widget gw;
363     XEvent *event;
364     String *params;
365     Cardinal *nparams;
366 {
367     MailboxWidget w = (MailboxWidget) gw;
368 
369     w->mailbox.last_size = -1;
370 
371     check_mailbox (w, TRUE, FALSE, TRUE);	/* redraw, no reset */
372 
373     return;
374 }
375 
376 
377 /*
378  * ack the existing mail; put widget in flagdown state
379  */
380 
381 /* ARGSUSED */
Ack(gw,event,params,nparams)382 static void Ack (gw, event, params, nparams)
383     Widget gw;
384     XEvent *event;
385     String *params;
386     Cardinal *nparams;
387 {
388     MailboxWidget w = (MailboxWidget) gw;
389 
390 	check_mailbox (w, TRUE, TRUE, TRUE);	/* redraw, reset */
391 
392     return;
393 }
394 
395 /*
396  * ack the existing mail; start mail-reader; put widget in flagdown state
397  */
398 
399 /* ARGSUSED */
Unset(gw,event,params,nparams)400 static void Unset (gw, event, params, nparams)
401     Widget gw;
402     XEvent *event;
403     String *params;
404     Cardinal *nparams;
405 {
406     MailboxWidget w = (MailboxWidget) gw;
407 
408 	check_mailbox (w, TRUE, TRUE, FALSE);	/* redraw, reset */
409 
410     return;
411 }
412 
413 
414 /*
415  * look to see if there is new mail; if so, Set, else Unset
416  */
417 
418 /* ARGSUSED */
Check(gw,event,params,nparams)419 static void Check (gw, event, params, nparams)
420     Widget gw;
421     XEvent *event;
422     String *params;
423     Cardinal *nparams;
424 {
425     MailboxWidget w = (MailboxWidget) gw;
426 
427     check_mailbox (w, TRUE, FALSE, TRUE);	/* redraw, no reset */
428 
429     return;
430 }
431 
432 
433 /* ARGSUSED */
clock_tic_anim(client_data,id)434 static void clock_tic_anim (client_data, id)
435     XtPointer client_data;
436     XtIntervalId *id;
437 {
438     MailboxWidget w = (MailboxWidget) client_data;
439 
440 	redraw_mailbox(w);
441     /*
442      * and reset the timer
443      */
444 
445     w->mailbox.anim_int_id =
446 		XtAppAddTimeOut (XtWidgetToApplicationContext((Widget) w),
447 				 w->mailbox.mail_animupdate, clock_tic_anim, client_data);
448 
449     return;
450 }
451 
452 /* ARGSUSED */
clock_tic(client_data,id)453 static void clock_tic (client_data, id)
454     XtPointer client_data;
455     XtIntervalId *id;
456 {
457     MailboxWidget w = (MailboxWidget) client_data;
458 
459     check_mailbox (w, FALSE, FALSE, TRUE);	/* no redraw, no reset */
460 
461     /*
462      * and reset the timer
463      */
464 
465     w->mailbox.interval_id =
466 	XtAppAddTimeOut (XtWidgetToApplicationContext((Widget) w),
467 			 w->mailbox.update * 1000, clock_tic, client_data);
468 
469     return;
470 }
471 
Realize(gw,valuemaskp,attr)472 static void Realize (gw, valuemaskp, attr)
473     Widget					gw;
474     XtValueMask				*valuemaskp;
475     XSetWindowAttributes	*attr;
476 {
477     MailboxWidget		w = (MailboxWidget) gw;
478     register Display	*dpy = XtDisplay (w);
479     XpmAttributes		xpm_attr;
480 	int					i;
481 
482     *valuemaskp |= (CWBitGravity | CWCursor);
483     attr->bit_gravity = ForgetGravity;
484     attr->cursor = XCreateFontCursor (dpy, XC_top_left_arrow);
485 
486     (*mailboxWidgetClass->core_class.superclass->core_class.realize)
487 	(gw, valuemaskp, attr);
488 
489     /*
490      * build the pixmaps for the two mailbox
491      * states from the saved XpmImages
492      */
493     xpm_attr.valuemask = 0;
494 
495 	for(i = 0; i < w->mailbox.mail_numofxpmfile; i++) {
496 		XpmCreatePixmapFromXpmImage (dpy, w->core.window,
497 									 w->mailbox.full[i].xpmimg,
498 									 &w->mailbox.full[i].pixmap,
499 									 &w->mailbox.full[i].bitmap, &xpm_attr);
500 	}
501 
502     xpm_attr.valuemask = 0;
503     XpmCreatePixmapFromXpmImage (dpy, w->core.window, w->mailbox.empty.xpmimg,
504 								 &w->mailbox.empty.pixmap,
505 								 &w->mailbox.empty.bitmap, &xpm_attr);
506 
507     w->mailbox.interval_id =
508 	XtAppAddTimeOut (XtWidgetToApplicationContext((Widget) w),
509 					 w->mailbox.update * 1000, clock_tic, (XtPointer) w);
510 
511     w->mailbox.shape_cache.mask = None;
512 
513     check_mailbox (w, TRUE, FALSE, TRUE);
514 
515     return;
516 }
517 
518 
Destroy(gw)519 static void Destroy (gw)
520     Widget gw;
521 {
522     MailboxWidget w = (MailboxWidget) gw;
523     Display *dpy = XtDisplay (gw);
524 	int	i;
525 
526     XtFree (w->mailbox.filename);
527     if (w->mailbox.interval_id) XtRemoveTimeOut (w->mailbox.interval_id);
528     if (w->mailbox.anim_int_id != -1) XtRemoveTimeOut (w->mailbox.anim_int_id);
529     XtReleaseGC(gw, w->mailbox.gc);
530 #define freepix(p) if (p) XFreePixmap (dpy, p)
531 	for(i = 0; i < w->mailbox.mail_numofxpmfile; i++) {
532 		freepix (w->mailbox.full[i].bitmap);	/* until cvter does ref cnt */
533 		freepix (w->mailbox.full[i].pixmap);
534 	}
535     freepix (w->mailbox.empty.bitmap);			/* until cvter does ref cnt */
536     freepix (w->mailbox.empty.pixmap);
537     freepix (w->mailbox.shape_cache.mask);
538 #undef freepix
539     return;
540 }
541 
542 
Redisplay(gw)543 static void Redisplay (gw)
544     Widget gw;
545 {
546     MailboxWidget w = (MailboxWidget) gw;
547 
548     check_mailbox (w, TRUE, FALSE, TRUE);
549 }
550 
551 
check_mailbox(w,force_redraw,reset,ackonly)552 static void check_mailbox (w, force_redraw, reset, ackonly)
553     MailboxWidget w;
554     Boolean force_redraw, reset, ackonly;
555 {
556     long mailboxsize = 0;
557     Boolean readSinceLastWrite = FALSE;
558     int pid;
559 
560     if (w->mailbox.check_command != NULL) {
561 		waitType wait_status;
562 		int	check_status;
563 #ifdef INTWAITTYPE
564 		wait_status = system(w->mailbox.check_command);
565 #else
566 		wait_status.w_status = system(w->mailbox.check_command);
567 #endif
568 		check_status = waitCode(wait_status);
569 
570 		/* error in sh checkCommand execution */
571 		if (waitSig(wait_status))
572 			check_status = 2;		/* act as if there is no mail */
573 
574 		switch (check_status) {
575 		  case 0:
576 			mailboxsize = w->mailbox.last_size + 1;
577 			break;
578 		  case 2:
579 			mailboxsize = 0;
580 			break;
581 		  default:		/* treat everything else as no change */
582 						/* case 1 is no change */
583 			mailboxsize = w->mailbox.last_size;
584 		}
585     } else {
586 		struct stat st;
587 		if (stat (w->mailbox.filename, &st) == 0) {
588 		    mailboxsize = st.st_size;
589 		    readSinceLastWrite = (st.st_atime > st.st_mtime);
590 		}
591     }
592 
593     /*
594      * Now check for changes.  If reset is set then we want to pretend that
595      * there is no mail.  If the mailbox is empty then we want to turn off
596      * the flag.  Otherwise if the mailbox has changed size then we want to
597      * put the flag up, unless the mailbox has been read since the last
598      * write.
599      *
600      * The cases are:
601      *    o  forced reset by user                        DOWN
602      *    o  no mailbox or empty (zero-sized) mailbox    DOWN
603      *    o  if read after most recent write             DOWN
604      *    o  same size as last time                      no change
605      *    o  bigger than last time                       UP
606      *    o  smaller than last time but non-zero         UP
607      *
608      * The last two cases can be expressed as different from last
609      * time and non-zero.
610      */
611 
612     if (reset) {						/* forced reset */
613 		w->mailbox.flag_up = FALSE;
614 		force_redraw = TRUE;
615 		if (!ackonly && w->mailbox.mail_tool) {
616 		    pid = fork();
617 		    if (pid == 0) {				/* Child process */
618 				system(w->mailbox.mail_tool);
619 				_exit(0);
620 		    }
621 		}
622     } else if (mailboxsize == 0) {		/* no mailbox or empty */
623 		w->mailbox.flag_up = FALSE;
624 		if (w->mailbox.last_size > 0)
625 			force_redraw = TRUE;		/* if change */
626     } else if (readSinceLastWrite) { 	/* only when checkCommand is NULL */
627 	/* mailbox has been read after most recent write */
628 		if (w->mailbox.flag_up) {
629 	    	w->mailbox.flag_up = FALSE;
630 	    	force_redraw = TRUE;
631 		}
632     } else if (mailboxsize != w->mailbox.last_size) {  /* different size */
633 		if (!w->mailbox.once_only || !w->mailbox.flag_up)
634 	    	beep(w);
635 		if (!w->mailbox.flag_up)
636 	    	force_redraw = w->mailbox.flag_up = TRUE;
637 	}
638 	w->mailbox.last_size = mailboxsize;
639 	if (force_redraw) redraw_mailbox (w);
640 	return;
641 }
642 
643 /*
644  * get user name for building mailbox
645  */
646 
GetMailFile(w)647 static void GetMailFile (w)
648     MailboxWidget w;
649 {
650     char *getlogin();
651     char *username;
652 
653     /* MAIL env var overrides any hard-coded mail dir */
654 	username = getenv( "MAIL" );
655 	if (username!=(char *)NULL) {
656 		w->mailbox.filename = (String) XtMalloc (strlen (username) + 1);
657 		strcpy (w->mailbox.filename, username);
658 		return;
659 	}
660 
661 	username = getlogin();
662 	if (!username) {
663 		struct passwd *pw = getpwuid (getuid ());
664 
665 		if (!pw) {
666 	    	fprintf (stderr, "%s:  unable to find a username for you.\n",
667 		     	"Mailbox widget");
668 	    	CloseDown (w, 1);
669 		}
670 		username = pw->pw_name;
671     }
672 	w->mailbox.filename = (String) XtMalloc (strlen (MAILBOX_DIRECTORY) + 1 +
673 											 strlen (username) + 1);
674 	strcpy (w->mailbox.filename, MAILBOX_DIRECTORY);
675 	strcat (w->mailbox.filename, "/");
676 	strcat (w->mailbox.filename, username);
677 	return;
678 }
679 
CloseDown(w,status)680 static void CloseDown (w, status)
681     MailboxWidget w;
682     int status;
683 {
684     Display *dpy = XtDisplay (w);
685 
686     XtDestroyWidget ((Widget)w);
687     XCloseDisplay (dpy);
688     exit (status);
689 }
690 
691 /*
692  * Called by Initialize() to read XPM icon files
693  * and create XPM image data from their contents
694  */
ReadIconFile(w,flag)695 static void ReadIconFile (w, flag)
696     MailboxWidget w;
697     Bool flag;
698 {
699     String			name;
700     char			aname[255], *ch=NULL;
701     int				code;
702     struct _mbimage	*im;
703     XpmImage		*imp;
704 	int				i;
705 
706     if ( flag ) {
707 		if (w->mailbox.mail_numofxpmfile > 1) {
708 			/* append a number before a file name */
709 			ch = strrchr(w->mailbox.mail_xpmfile, '/');
710 			if (ch) *ch = '\0';
711 		}
712 		for(i = 0; i < w->mailbox.mail_numofxpmfile; i++) {
713 			if (w->mailbox.mail_numofxpmfile > 1)
714 				sprintf(aname, "%s/%d%s", w->mailbox.mail_xpmfile, i, ch+1);
715 			else
716 				strcpy(aname, w->mailbox.mail_xpmfile);	/* 1: file for "mail" */
717 			im = &w->mailbox.full[i];
718 			imp = &mail_xpmimg[i];
719 			code = XpmReadFileToXpmImage( aname, imp, (XpmInfo *)NULL );
720 			if ( code == XpmSuccess ) {
721 				im->xpmimg = imp;
722 				im->width = imp->width;
723 				im->height = imp->height;
724 			}
725 			else
726 			{
727 				fprintf (stderr, "%s: ReadIconFile(%s): %s\n",
728 						 "Mailbox widget", aname, XpmGetErrorString(code));
729 				w->mailbox.mail_numofxpmfile = i;
730 				if (w->mailbox.mail_numofxpmfile == 0)
731 					w->mailbox.mail_numofxpmfile = 1;
732 				break;
733 			}
734 		}
735     }
736     else {
737 		name = w->mailbox.nomail_xpmfile; /* False: file for no "mail" */
738 		im = &w->mailbox.empty;
739 		imp = &nomail_xpmimg;
740 		code = XpmReadFileToXpmImage( name, imp, (XpmInfo *)NULL );
741 		if ( code == XpmSuccess ) {
742 			im->xpmimg = imp;
743 			im->width = imp->width;
744 			im->height = imp->height;
745 		}
746 		else
747 			fprintf (stderr, "%s: ReadIconFile(%s): %s\n",
748 				 "Mailbox widget", name, XpmGetErrorString(code));
749     }
750 }
751 
752 /*
753  * Called by Initialize() to create XPM image
754  * data from the default (hard-coded) XPM icons
755  */
DefaultXpmImage(w,flag)756 static void DefaultXpmImage(w, flag)
757     MailboxWidget w;
758     Bool flag;
759 {
760 #define MakeXpmImage()	{ \
761 		code = XpmCreateXpmImageFromData (datap, imp, (XpmInfo *)NULL); \
762 		if ( code == XpmSuccess ) { \
763 			im->xpmimg = imp; \
764 			im->width = imp->width; \
765 			im->height = imp->height; \
766 		} \
767 		else { \
768 			fprintf (stderr, "%s: DefaultXpmImage(): %s\n", \
769 					 "Mailbox widget", XpmGetErrorString(code)); \
770 			CloseDown (w, 1); \
771 		} \
772 	}
773 
774     char			**datap;
775     int				code;
776     struct _mbimage	*im;
777     XpmImage		*imp;
778 	int				i;
779 
780     if ( flag ) {
781 		for(i = 0; i < w->mailbox.mail_numofxpmfile; i++) {
782 			datap = (i % 2)? xnomail_xpm: xmail_xpm; /* 1: data for "mail" */
783 			im = &w->mailbox.full[i];
784 			imp = &mail_xpmimg[i];
785 			MakeXpmImage();
786 		}
787     }
788     else {
789 		datap = xnomail_xpm; /* False: data for "no mail" */
790 		im = &w->mailbox.empty;
791 		imp = &nomail_xpmimg;
792 		MakeXpmImage();
793     }
794 #undef MakeXpmImage
795 }
796 
797 /* ARGSUSED */
SetValues(gcurrent,grequest,gnew)798 static Boolean SetValues (gcurrent, grequest, gnew)
799     Widget gcurrent, grequest, gnew;
800 {
801     MailboxWidget current = (MailboxWidget) gcurrent;
802     MailboxWidget new = (MailboxWidget) gnew;
803     Boolean redisplay = FALSE;
804 
805     if (current->mailbox.update != new->mailbox.update) {
806 		if (current->mailbox.interval_id)
807 		XtRemoveTimeOut (current->mailbox.interval_id);
808 		new->mailbox.interval_id =
809 			XtAppAddTimeOut (XtWidgetToApplicationContext(gnew),
810 							 new->mailbox.update * 1000, clock_tic,
811 							 (XtPointer) gnew);
812     }
813 
814     return (redisplay);
815 }
816 
817 
818 /*
819  * drawing code
820  */
821 
redraw_mailbox(w)822 static void redraw_mailbox (w)
823     MailboxWidget w;
824 {
825 	static				int ctrig=0;
826     register Display	*dpy = XtDisplay (w);
827     register Window		win = XtWindow (w);
828 	XWindowAttributes	wa;
829     GC					gc = w->mailbox.gc;
830     struct _mbimage		*im;
831     Widget				parent;
832 	int					wpx, wpy;
833 
834     if (w->mailbox.flag_up) {		/* draw the "mail" icon */
835 		if (w->mailbox.mail_numofxpmfile > 1) {
836 			if (w->mailbox.first_trig) {
837 				w->mailbox.anim_int_id = XtAppAddTimeOut (
838 							XtWidgetToApplicationContext((Widget) w),
839 							w->mailbox.mail_animupdate, clock_tic_anim,
840 							(XtPointer) w);
841 				w->mailbox.anim_id = 0;
842 				w->mailbox.first_trig = 0;
843 				ctrig = 0;
844 			} else {
845 				/* This route called twice (The second one from ReDisplay()) */
846 				if ((++ctrig % 2) == 0) {
847 					w->mailbox.anim_id = (w->mailbox.anim_id + 1)
848 											% (w->mailbox.mail_numofxpmfile);
849 					if (w->mailbox.mail_animonce && (w->mailbox.anim_id == 0)) {
850 						if (w->mailbox.anim_int_id != -1) {
851 							XtRemoveTimeOut(w->mailbox.anim_int_id);
852 							w->mailbox.anim_int_id = -1;
853 						}
854 						w->mailbox.anim_id = w->mailbox.mail_numofxpmfile - 1;
855 					}
856 					ctrig = 0;
857 				}
858 
859 				if (w->mailbox.anim_int_id == -1)
860 					return;
861 			}
862 			im = &(w->mailbox.full[w->mailbox.anim_id]);
863 		}
864 		else im = &(w->mailbox.full[0]);
865 	} else {						/* draw the "no mail" icon */
866 		im = &w->mailbox.empty;
867 		w->mailbox.first_trig = 1;
868 		if (w->mailbox.anim_int_id != -1) {
869 			XtRemoveTimeOut(w->mailbox.anim_int_id);
870 			w->mailbox.anim_int_id = -1;
871 		}
872 	}
873     XClearWindow (dpy, win);
874 	XGetWindowAttributes(dpy, win, &wa);
875 	wpx = (wa.width - im->width) / 2;   wpx = (wpx<0)? 0 : wpx;
876 	wpy = (wa.height - im->height) / 2; wpy = (wpy<0)? 0 : wpy;
877     XCopyArea (dpy, im->pixmap, win, gc, 0, 0, im->width, im->height, wpx, wpy);
878 
879     /*
880      * XXX - temporary hack; walk up widget tree to find top most parent (which
881      * will be a shell) and mash it to have our shape.  This will be replaced
882      * by a special shell widget.
883      */
884 
885 	for (parent = (Widget)w; XtParent(parent);
886 		 parent = XtParent(parent));
887 
888     if (im->bitmap != w->mailbox.shape_cache.mask ||
889 		wpx != w->mailbox.shape_cache.x ||
890 		wpy != w->mailbox.shape_cache.y)
891 	{
892 		XShapeCombineMask (XtDisplay(parent), XtWindow(parent),
893 						   ShapeBounding, wpx, wpy, im->bitmap, ShapeSet);
894 		w->mailbox.shape_cache.mask = im->bitmap;
895 		w->mailbox.shape_cache.x = wpx;
896 		w->mailbox.shape_cache.y = wpy;
897 	}
898 
899     return;
900 }
901 
902 #ifdef COM_AUDIO
comm_play(command,soundfile)903 static int comm_play (command, soundfile)
904 	char *command;
905 	char *soundfile;
906 {
907 	char comm[255]; /* we suppose the command will be smaller than 255 chars */
908 	int  result;
909 
910 	if ( ( command == NULL ) || ( soundfile == NULL ) )
911 		return -1;
912 
913 	sprintf(comm, command, soundfile);
914 
915 	return system(comm);
916 }
917 #endif /* COM_AUDIO */
918 
919 #ifndef NO_AUDIO
920 #define CLOSE_FD(afd)	{ if (afd > -1) close(afd); afd = -1; }
921 
922 #if defined(linux) || defined(__FreeBSD__)
923 #define INIT_FD	{ audiofd = filefd = mixer_fd = -1; }
924 #define END_FD	{ CLOSE_FD(audiofd); CLOSE_FD(filefd); CLOSE_FD(mixer_fd); return; }
925 #else
926 #define INIT_FD	{ audiofd = filefd = 0; }
927 #define END_FD	{ CLOSE_FD(audiofd); CLOSE_FD(filefd); return; }
928 #endif /* if defined(linux) */
929 #endif /* ifndef NO_AUDIO  */
930 
beep(w)931 static void beep (w)
932     MailboxWidget w;
933 {
934 #ifndef NO_AUDIO
935 #ifdef COM_AUDIO
936 	if (comm_play(w->mailbox.mail_sndcomm, w->mailbox.mail_sndfile))
937 #else
938 #ifdef NCD_AUDIO
939 	char	*auservername = NULL;
940 #elif defined(RPLAY_AUDIO)
941 	char	*rplay_name;
942 #endif /* ifdef NCD_AUDIO */
943 	int		audiofd, filefd;
944 	int		rn, wn, len;
945 	unsigned char	buf[256];
946 #ifdef SUN_AUDIO
947 	Audio_filehdr	*au_hdr;
948 #if defined(linux) || defined(__FreeBSD__)
949 	StereoVolume origVol, volume;
950 	int				mixer_fd;
951 #else
952 	audio_info_t 	ais;
953 	int				origVol;
954 #endif /* if defined(linux) */
955 #endif /* ifdef SUN_AUDIO */
956 
957 	if (w->mailbox.mail_sndfile) {
958 #ifdef NCD_AUDIO
959 		aud = AuOpenServer(auservername, 0, NULL, 0, NULL, NULL);
960 		if (aud) {
961 			if (!AuSoundPlaySynchronousFromFile(aud, w->mailbox.mail_sndfile,
962 												w->mailbox.volume))
963 				fprintf(stderr, "%s: Couldn't play file \"%s\"\n",
964 						"Mailbox widget", w->mailbox.mail_sndfile);
965 
966 			AuCloseServer(aud);
967 			return;
968 		}
969 #elif defined(RPLAY_AUDIO)
970 		rplay_name = rplay_default_host();
971 		if (rplay_host_volume(rplay_name, w->mailbox.mail_sndfile,
972 							  (int)(w->mailbox.volume*2.55)) < 0)
973 			fprintf(stderr, "%s: Rplay couldn't play file \"%s\"\n",
974 					"Mailbox widget", w->mailbox.mail_sndfile);
975 		else
976 			return;
977 #endif /* ifdef NCD_AUDIO */
978 #ifdef SUN_AUDIO
979 		INIT_FD;
980 		audiofd = open( "/dev/audio", O_WRONLY | O_NDELAY );
981 		if (audiofd < 0) {
982 			fprintf(stderr, "%s: Problem opening /dev/audio.\n",
983 					"Mailbox widget");
984 			END_FD;
985 		}
986 #if defined(linux) || defined(__FreeBSD__)
987 		if ( (mixer_fd=open(DEV_MIXER, O_RDWR, 0)) < 0 ) {
988 			fprintf(stderr, "Can't open %s: ", DEV_MIXER);
989 			END_FD;
990 		}
991 
992 		if ( ioctl(mixer_fd, SOUND_MIXER_READ_PCM, &origVol) < 0 ) {
993 			perror("Can't obtain current volume settings");
994 			END_FD;
995 		}
996 
997 		setvolume(LEFT|RIGHT, (unsigned char)w->mailbox.volume, &volume);
998 
999 		if ( ioctl(mixer_fd, SOUND_MIXER_WRITE_PCM, &volume) < 0 ) {
1000 			fprintf(stderr, "Can't set current volume settings");
1001 			END_FD;
1002 		}
1003 
1004 		/* The following is required for the setting to take into effect
1005 		CLOSE_FD(mixer_fd); */
1006 #else
1007 		if( ioctl( audiofd, AUDIO_GETINFO, &ais ) ) {
1008 			fprintf(stderr, "%s: Problem retrieving /dev/audio info.\n",
1009 					"Mailbox widget");
1010 			END_FD;
1011 		}
1012 		origVol = ais.play.gain;
1013 		ais.play.gain = w->mailbox.volume;
1014 		if( ioctl( audiofd, AUDIO_SETINFO, &ais ) ) {
1015 			fprintf(stderr, "%s: Problem setting /dev/audio info.\n",
1016 					"Mailbox widget");
1017 			END_FD;
1018 		}
1019 #endif /* if defined(linux) */
1020 		filefd = open(w->mailbox.mail_sndfile, O_RDONLY);
1021 		if (filefd < 0) {
1022 			fprintf(stderr, "%s: Couldn't play file \"%s\"\n",
1023 				"Mailbox widget", w->mailbox.mail_sndfile);
1024 			END_FD;
1025 		}
1026 
1027 		/* Read in the audio header */
1028 		rn = read(filefd, buf, sizeof(Audio_filehdr));
1029 
1030 		if (rn > 0 && strncmp(buf, ".snd", 4) != 0) {
1031 			fprintf(stderr, "%s: Invalid audio file format.\n",
1032 					"Mailbox widget");
1033 			END_FD;
1034 		}
1035 
1036 		/* Strip the header */
1037 		au_hdr = (Audio_filehdr *)buf;
1038 #if defined(linux) || defined(__FreeBSD__)
1039 		rn = ntohl(au_hdr->hdr_size) - sizeof(Audio_filehdr);
1040 #else
1041 		rn = au_hdr->hdr_size - sizeof(Audio_filehdr);
1042 #endif /* if defined(linux) */
1043 		for( ; rn > 0; ) {
1044 			len = min(rn, sizeof(buf));
1045 			len = read(filefd, buf, len);
1046 			rn -= len;
1047 		}
1048 
1049 		while(1) {
1050 			rn = read(filefd, buf, sizeof(buf));
1051 			if (rn < 0) {
1052 				fprintf(stderr, "%s: Error reading from file \"%s\"\n",
1053 					"Mailbox widget", w->mailbox.mail_sndfile);
1054 				END_FD;
1055 			}
1056 			if (rn == 0)
1057 				break;
1058 			while(1) {
1059 				wn = write(audiofd, buf, rn);
1060 				if ( wn < 0 ) {
1061 					fprintf(stderr, "%s: Error writing to /dev/audio.\n",
1062 						"Mailbox widget");
1063 					END_FD;
1064 				}
1065 				if ( wn != 0 )
1066 					break;
1067 				usleep(1000);
1068 			}
1069 		}
1070 #if defined(linux) || defined(__FreeBSD__)
1071 		CLOSE_FD(audiofd);
1072 
1073 		if ( ioctl(mixer_fd, SOUND_MIXER_WRITE_PCM, &origVol) < 0 ) {
1074 			fprintf(stderr, "Can't reset volume settings");
1075 		}
1076 #else
1077 		ais.play.gain = origVol;
1078 		if( ioctl( audiofd, AUDIO_SETINFO, &ais ) ) {
1079 			fprintf(stderr, "%s: Problem setting /dev/audio info.\n",
1080 					"Mailbox widget");
1081 		}
1082 #endif /* if defined(linux) */
1083 		END_FD;
1084 #endif /* #ifdef SUN_AUDIO */
1085 	}
1086 	else
1087 #endif /* #ifdef COM_AUDIO */
1088 #endif /* #ifndef NO_AUDIO */
1089 		XBell (XtDisplay (w), w->mailbox.volume);
1090     return;
1091 }
1092 
1093