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