/* coolmail.c
*
* A cool mail notification utility.
*
* By Byron C. Darrah
* Created: 1-June-1994, Wed
* Modified: 19-Oct-1994, Tue
* Modified: 30-Dec-1994, Fri
* Modified: 3-Jan-1994, Tue
* Modified: 9-Feb-1995, Thurs
* Modified: 21-Feb-1995, Tue
* Modified: 01-Sept-95, Fri (Added sound support --Randy Sharpe)
* Modified: 19-Sep-1995, Tue
*
*/
#define Version "1.3"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#ifdef AUDIO
#include <string.h>
#endif
#include "render1.h"
#include "mailbox.h"
#define DEFAULT_MAIL_DIR "/var/mail/"
#define DEFAULT_COMMAND "xterm -n Elm -e elm\0"
#define DEFAULT_INTERVAL 30
#define DEFAULT_FRAMES 15
#ifdef AUDIO
#define DEFAULT_VOLUME 50
#endif
#ifndef PI
#define PI 3.1415926536
#endif
polygon_t flag[2], cube2[5], letter[1], post[6], ground[1];
int_point_t flag_ints[2][4],
cube2_ints[5][4],
letter_ints[1][4],
post_ints[6][4],
ground_ints[1][4];
char command_str[1024] = DEFAULT_COMMAND;
char mailfile_str[1024] = DEFAULT_MAIL_DIR;
unsigned long interval = DEFAULT_INTERVAL*1000;
unsigned int frames = DEFAULT_FRAMES;
int verbose = 0;
#ifdef AUDIO
char *sndfile = NULL; /* default system sound */
int cool_vol = DEFAULT_VOLUME;
#endif
float flag_angle = 0.0;
int Mailbox_Is_Open = 0,
envelope_present = 0,
alerted = 0,
onceonly = 0;
void rotate_in(unsigned int steps);
void rotate_out(unsigned int steps);
void define_shapes(void);
void print_camera(real_point_t pos, real_point_t foc, real_point_t up);
void cool_parseargs(int argc, char *argv[]);
void cool_init(void);
void cool_checkmail(void);
int cool_do_command(char *command);
void cool_raise_flag(unsigned int steps);
void cool_lower_flag(unsigned int steps);
void cool_put_envelope(void);
void cool_remove_envelope(void);
void cool_rotate(float *px, float *py, float cx, float cy, float angle);
void cool_get_inboxstatus(char *filename, int *anymail, int *unreadmail,
int *newmail);
void cool_closemailbox(int n);
void cool_alarm(void);
int cool_help(int argc, char *argv[]);
void cool_usage(void);
/*---------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
int reason;
#ifndef NO_CUSERID
char username[L_cuserid];
#endif
/* Quickly scan for the -h option -- if it is present don't do anything
* but print out some help and exit. */
if (cool_help(argc, argv))
return(0);
/* Get the username and use it to create a default mailfile name */
#ifdef SUPPORT_MAILDIR
if (getenv("MAILDIR") && strlen(getenv("MAILDIR"))) {
strcpy(mailfile_str,getenv("MAILDIR"));
} else if (getenv("MAIL") && strlen(getenv("MAIL"))) {
strcpy(mailfile_str,getenv("MAIL"));
} else
#endif
#ifndef NO_CUSERID
strcat(mailfile_str, cuserid(username));
#else
strcat(mailfile_str, getlogin());
#endif
/* Initialize the renderer */
rend_init(&argc, argv, (float)150.0);
cool_parseargs(argc, argv);
if (verbose)
printf("Coolmail %s watching file: %s\n", Version, mailfile_str);
define_shapes();
rend_register_obj(1, 2);
rend_register_obj(2, 5);
rend_register_obj(3, 1);
rend_register_obj(4, 5);
rend_register_obj(5, 1);
rend_submit_plist(1, flag);
rend_submit_plist(2, cube2);
rend_submit_plist(4, post);
rend_submit_plist(5, ground);
cool_init();
reason = rend_freeze(interval);
while(1)
{
switch(reason)
{
case TIMEOUT:
cool_checkmail();
break;
case SELECTION:
rotate_in(frames);
reason = cool_do_command(command_str);
alerted = 0;
continue;
break;
default:
fprintf(stderr, "coolmail: Renderer unfroze for unknown reason.\n");
assert(0);
}
reason = rend_freeze(interval);
}
printf("Reason for termination: %s\n", reason == TIMEOUT ?
"Timed out" : "Selected");
rend_end();
return(0);
}
/*---------------------------------------------------------------------------*/
void rotate_in(unsigned int steps)
{
float theta, dist;
int i;
float frames = (float)steps;
static float mintheta = 0.4;
static float maxtheta = 1.7;
static float mindist = 270.0;
static float maxdist = 150.0;
real_point_t cam_pos = {60.0, 96.0, -20.0},
cam_foc = {60.0, 96.0, 260.0},
cam_up = {60.0, 99.0, -20.0};
for (i = 0; i < steps; i++)
{
theta = i * (maxtheta - mintheta)/ frames + mintheta;
dist = i * (maxdist - mindist) / frames + mindist;
cam_pos.z = -dist * cos((double)theta) + cam_foc.z;
cam_pos.x = dist * sin((double)theta) + cam_foc.x;
cam_up.z = cam_pos.z;
cam_up.x = cam_pos.x;
rend_set_camera(&cam_pos, &cam_foc, &cam_up);
rend_transform_update_all();
rend_frame();
}
}
/*---------------------------------------------------------------------------*/
void rotate_out(unsigned int steps)
{
float theta, dist;
int i;
float frames = (float)steps;
static float mintheta = 0.4;
static float maxtheta = 1.7;
static float mindist = 270.0;
static float maxdist = 150.0;
real_point_t cam_pos = {60.0, 96.0, -20.0},
cam_foc = {60.0, 96.0, 260.0},
cam_up = {60.0, 99.0, -20.0};
for (i = steps - 1; i >= 0; i--)
{
theta = i * (maxtheta - mintheta)/ frames + mintheta;
dist = i * (maxdist - mindist) / frames + mindist;
cam_pos.z = -dist * cos((double)theta) + cam_foc.z;
cam_pos.x = dist * sin((double)theta) + cam_foc.x;
cam_up.z = cam_pos.z;
cam_up.x = cam_pos.x;
rend_set_camera(&cam_pos, &cam_foc, &cam_up);
rend_transform_update_all();
rend_frame();
}
}
/*---------------------------------------------------------------------------*/
void print_camera(real_point_t pos, real_point_t foc, real_point_t up)
{
printf("cam_pos = (%6.2f, %6.2f, %6.2f)\n", pos.x, pos.y, pos.z);
printf("cam_foc = (%6.2f, %6.2f, %6.2f)\n", foc.x, foc.y, foc.z);
printf("cam_up = (%6.2f, %6.2f, %6.2f)\n", up.x, up.y, up.z);
}
/*---------------------------------------------------------------------------*/
void define_shapes(void)
{
/* Define the letter */
letter[0].color = WHITE;
letter[0].sim_points = envelope;
letter[0].npoints = 4;
letter[0].int_points = letter_ints[0];
/* Define the ground */
ground[0].color = GREEN;
ground[0].sim_points = ground_plane;
ground[0].npoints = 4;
ground[0].int_points = ground_ints[0];
/* Define the mailbox flag */
flag[0].color = RED;
flag[0].sim_points = flag_1;
flag[0].npoints = 4;
flag[0].int_points = flag_ints[0];
flag[1].color = RED;
flag[1].sim_points = flag_2;
flag[1].npoints = 4;
flag[1].int_points = flag_ints[1];
/* Define the mailbox itself */
cube2[0].color = DARKCYAN;
cube2[0].sim_points = mailbox_front; /* Front */
cube2[0].npoints = 4;
cube2[0].int_points = cube2_ints[0];
cube2[1].color = GREY2;
cube2[1].sim_points = mailbox_back; /* Back */
cube2[1].npoints = 4;
cube2[1].int_points = cube2_ints[1];
cube2[2].color = CYAN;
cube2[2].sim_points = mailbox_top; /* Top */
cube2[2].npoints = 4;
cube2[2].int_points = cube2_ints[2];
cube2[3].color = DARKBLUE;
cube2[3].sim_points = mailbox_bottom; /* Bottom */
cube2[3].npoints = 4;
cube2[3].int_points = cube2_ints[3];
cube2[4].color = GREY1;
cube2[4].sim_points = mailbox_left; /* Left */
cube2[4].npoints = 4;
cube2[4].int_points = cube2_ints[4];
/* Define the post */
post[0].color = DARKBROWN;
post[0].sim_points = post_bottom; /* Bottom face */
post[0].npoints = 4;
post[0].int_points = post_ints[1];
post[1].color = DARKBROWN;
post[1].sim_points = post_front; /* Front face */
post[1].npoints = 4;
post[1].int_points = post_ints[2];
post[2].color = DARKBROWN;
post[2].sim_points = post_back; /* Back face */
post[2].npoints = 4;
post[2].int_points = post_ints[3];
post[3].color = BROWN;
post[3].sim_points = post_right; /* Right face */
post[3].npoints = 4;
post[3].int_points = post_ints[4];
post[4].color = DARKBROWN;
post[4].sim_points = post_left; /* Left face */
post[4].npoints = 4;
post[4].int_points = post_ints[5];
}
/*---------------------------------------------------------------------------*/
void cool_init(void)
{
rotate_out(2); /* Initialize the display */
cool_checkmail(); /* Check whether to raise flag & beep */
}
/*---------------------------------------------------------------------------*/
/* Check for the existence of new mail.
* If there is new mail, raise the flag and sound the alarm.
*
* This function could probably be made a lot smarter.
*/
void cool_checkmail(void)
{
time_t newsize;
int anymail, newmail, unreadmail;
cool_get_inboxstatus(mailfile_str, &anymail, &unreadmail, &newmail);
if (anymail)
cool_put_envelope();
else
cool_remove_envelope();
if (unreadmail)
cool_raise_flag(frames);
else
cool_lower_flag(frames);
if (newmail)
cool_alarm();
}
/*---------------------------------------------------------------------------*/
void cool_parseargs(int argc, char *argv[])
{
int i = 1;
while (i < argc)
{
if (!strcmp(argv[i], "-f"))
{
i++;
strncpy(mailfile_str, argv[i], 1023);
}
else if (!strcmp(argv[i], "-e"))
{
i++;
strncpy(command_str, argv[i], 1023);
}
else if (!strcmp(argv[i], "-fr"))
{
i++;
sscanf(argv[i], "%u", &frames);
if(frames < 2) frames = 2;
}
else if (!strcmp(argv[i], "-int") || !strcmp(argv[i], "-update"))
{
i++;
sscanf(argv[i], "%lu", &interval);
interval *= 1000;
}
else if (!strcmp(argv[i], "-once"))
{
onceonly = 1;
}
else if (!strcmp(argv[i], "-v"))
{
verbose++;
}
#ifdef AUDIO
else if (!strcmp(argv[i], "-vol"))
{
i++;
sscanf(argv[i], "%u", &cool_vol);
if (cool_vol < 0) cool_vol = 0;
else if (cool_vol > 100) cool_vol = 100;
}
else if (!strcmp(argv[i], "-af"))
{
i++;
sndfile = strdup(argv[i]);
}
#endif
else
{
fprintf(stderr, "Unknown argument: %s\n", argv[i]);
fprintf(stderr, "Type coolmail -h for help.\n");
exit(0);
}
i++;
}
}
/*---------------------------------------------------------------------------*/
/* Check for the -h option. If found, print help and return nonzero. */
int cool_help(int argc, char *argv[])
{
int i;
for(i = 1; i < argc; i++)
if (!strcmp(argv[i], "-h"))
{
cool_usage();
return(1);
}
return(0);
}
/*---------------------------------------------------------------------------*/
void cool_usage(void)
{
printf("Usage: coolmail [options]\n\n");
printf("OPTIONS:\n");
printf(" -af filename Specifies an audio file (.au format) to use\n");
printf(" instead of the console bell.\n\n");
printf(" -e command Specifies a command (usually in quotes) which\n");
printf(" is used to invoke your favorite mail-reading\n");
printf(" program.\n\n");
printf(" -f filename Watch filename/maildir, instead of the default mail\n");
printf(" file, %s<username>.\n\n", DEFAULT_MAIL_DIR);
printf(" -fr n Number of frames to generate for each animation.\n");
printf(" Set to an appropriate value for your machine's.\n");
printf(" graphics performance. The default is %d.\n\n",
DEFAULT_FRAMES);
printf(" -h Print some help then exit.\n\n");
printf(" -int n Check mail every n seconds, instead of the\n");
printf(" default, %d seconds. Note: Xbiff fans may use\n",
DEFAULT_INTERVAL);
printf(" -update instead of -int.\n\n");
printf(" -mono Monochrome mode.\n\n");
printf(" -once Ring bell when new mail is first detected, but\n");
printf(" not when more new mail is detected.\n\n");
printf(" -v Verbose mode - Coolmail prints some information\n");
printf(" on startup.\n\n");
printf(" -vol n Value between 0 and 100 for the volume setting\n");
printf(" for both the console bell and the audio file.\n");
printf(" The default is 50.\n\n");
printf(" In addition, standard X intrinsic arguments are supported.\n");
}
/*---------------------------------------------------------------------------*/
/* Fork off the renderer update routine, to keep the mailbox window
* up to date while we execute the command.
*/
int cool_do_command(char *command)
{
int exists, reason;
time_t newsize;
pid_t child;
disp_sync();
if (child = fork())
{ /* Parent Process */
Mailbox_Is_Open = 1;
signal(SIGCHLD, cool_closemailbox);
while(Mailbox_Is_Open)
{
reason = rend_freeze(interval);
cool_checkmail();
}
wait(NULL);
return(reason);
}
else
{ /* Child Process */
pid_t parent = getppid();
system(command);
kill(parent, SIGCHLD);
exit(0);
}
}
/*---------------------------------------------------------------------------*/
void cool_raise_flag(unsigned int steps)
{
static float pivot_x = 34.0;
static float pivot_y = 80.0;
float inc = (PI/2.0)/(float)steps;
int poly, point;
if (flag_angle > PI/2.0 - inc) return;
for (; flag_angle < PI/2.0; flag_angle += inc)
{
for(poly = 0; poly < 2; poly++)
for(point = 0; point < 4; point++)
{
cool_rotate(&(flag[poly].sim_points[point].x),
&(flag[poly].sim_points[point].y),
pivot_x, pivot_y, inc);
}
rend_rm_plist(1);
rend_submit_plist(1, flag);
rend_frame();
}
return;
}
/*---------------------------------------------------------------------------*/
void cool_lower_flag(unsigned int steps)
{
static float pivot_x = 34.0;
static float pivot_y = 80.0;
float inc = (PI/2.0)/(float)steps;
int poly, point;
if (flag_angle < inc) return;
for (; flag_angle > 0.0; flag_angle -= inc)
{
for(poly = 0; poly < 2; poly++)
for(point = 0; point < 4; point++)
{
cool_rotate(&(flag[poly].sim_points[point].x),
&(flag[poly].sim_points[point].y),
pivot_x, pivot_y, -inc);
}
rend_rm_plist(1);
rend_submit_plist(1, flag);
rend_frame();
}
return;
}
/*---------------------------------------------------------------------------*/
/* Put the envelope in the mailbox */
void cool_put_envelope(void)
{
if (envelope_present) return;
rend_submit_plist(3, letter);
rend_frame();
envelope_present = 1;
}
/*---------------------------------------------------------------------------*/
/* Remove the envelope from the mailbox */
void cool_remove_envelope(void)
{
if (!envelope_present) return;
rend_rm_plist(3);
rend_frame();
envelope_present = 0;
}
/*---------------------------------------------------------------------------*/
/* Sound an alarm to alert the user of new mail */
void cool_alarm(void)
{
if (!alerted || !onceonly)
{
rend_bell();
alerted++;
}
}
/*---------------------------------------------------------------------------*/
/* Rotate (px,py) about the point (cx,cy).
*/
void cool_rotate(float *px, float *py, float cx, float cy, float angle)
{
double theta;
double r;
double xdist = (double)(*px-cx),
ydist = (double)(*py-cy);
r = sqrt((double)(xdist*xdist + ydist * ydist));
if (r == 0) return;
theta = acos(xdist/r);
if (asin(ydist/r) < 0.0)
theta = (2.0*PI - theta);
theta += (double)angle;
*px = r*cos(theta) + cx;
*py = r*sin(theta) + cy;
}
/*---------------------------------------------------------------------------*/
/* This is called upon receiving a signal from the child process, that the
* mail application has completed.
*/
void cool_closemailbox(int n)
{
cool_checkmail();
rotate_out(frames); /* Do the animation of the mailbox closing */
signal(SIGCHLD, SIG_DFL);
Mailbox_Is_Open = 0;
}
/*---------------------------------------------------------------------------*/
/* Get file modification time */
/* Maildir notes (aqua@sonoma.net, Sun Jan 18 19:42:27 PST 1998):
*
* The maildir mail-storage standard is a replacement for the traditional
* 'mbox' format, intended to remove problems with file contention, locking,
* reduce corruption in the case of a program or system crash, etc, etc.
* Fairly detailed description of it can be had as part of the Qmail MTA
* documentation, http://www.qmail.org/qmail-manual-html/man5/maildir.html.
*
* The general gist of the maildir approach is that mail is stored, one
# message per file, in a subtree of ~/Maildir. New mail goes in /new,
* the "spool" goes in /cur, and /tmp is available to MUAs &c. Mail is
* theoretically supposed to be removed from /new immediately by the
* MUA, but I've observed that with mutt 0.88, at least, it isn't if
* the mailfile was generated by an import script (e.g. mbox2maildir)
* rather than the normal delivery agent, presumably due to naming
* differences.
*
* Checking for new mail mostly entails checking the mtime vs. atime of
* every file in /new, and the number of messages in /new; if the latter
* increases, new mail was delivered -- if not, but the files' atimes
* are all later than their mtimes, the MUA read the /new spool.
*
* The specifications suggest skipping over every .file, but reading all
* the others -- I've extended this to include skipping of all non-regular
* files, which seemed to make sense -- define SUPPORT_MAILDIR_STRICTER to
* override this behavior.
*
* This process is more resource-intensive than the old scheme of merely
* calling stat() for a single file -- it's an O(n) rather than O(1)
* operation.
*
*/
void cool_get_inboxstatus(char *filename, int *anymail, int *unreadmail,
int *newmail)
{
static off_t oldsize = 0;
off_t newsize;
struct stat st;
int fd;
#ifdef SUPPORT_MAILDIR
DIR *d;
struct dirent *de;
char maildir[256],mfn[256];
#endif
#ifdef SUPPORT_MAILDIR_DEBUG
printf("B anymail=%d, newmail=%d, unreadmail=%d, oldsize=%d, newsize=%d\n",
*anymail,*newmail,*unreadmail,oldsize,newsize);
#endif
#ifdef SUPPORT_MAILDIR
if (stat(filename,&st)==-1) {
*anymail = *newmail = *unreadmail = 0;
newsize = oldsize = 0;
perror(filename);
return;
}
if (S_ISDIR(st.st_mode)) {
/* likely a maildir */
strcpy(maildir,filename);
if (maildir[strlen(maildir)-1]!='/')
strcat(maildir,"/");
strcat(maildir,"new");
if (stat(maildir,&st)==-1) {
perror(maildir);
printf("%s is not a maildir (missing/inaccessible %s)\n",filename,maildir);
*anymail = *newmail = *unreadmail = 0;
newsize = oldsize = 0;
return;
}
if (!S_ISDIR(st.st_mode)) {
printf("%s is not a directory (mode %d)\n",maildir,st.st_mode);
*anymail = *newmail = *unreadmail = 0;
newsize = oldsize = 0;
return;
}
d=opendir(maildir);
newsize=0;
*unreadmail = 0;
while ((de=readdir(d))) {
if (de->d_name[0]=='.') /* dotfiles ignored per the maildir specs */
continue;
strcpy(mfn,maildir);
if (mfn[strlen(mfn)-1]!='/')
strcat(mfn,"/");
strcat(mfn,de->d_name);
if (stat(mfn,&st)==-1) {
perror(mfn);
continue;
}
#ifndef SUPPORT_MAILDIR_STRICTER
if (S_ISREG(st.st_mode))
#endif
newsize++;
if (st.st_mtime>=st.st_atime) {
#ifdef SUPPORT_MAILDIR_DEBUG
printf("unread: %s mtime = %d, atime = %d\n",de->d_name,st.st_mtime,st.st_atime);
#endif
*unreadmail = 1;
}
}
closedir(d);
if (newsize) {
*anymail = 1;
if (newsize>oldsize && *unreadmail)
*newmail = 1;
else
*newmail = 0;
} else {
*anymail = *newmail = *unreadmail = 0;
newsize = 0;
}
#ifdef SUPPORT_MAILDIR_DEBUG
printf("A anymail=%d, newmail=%d, unreadmail=%d, oldsize=%d, newsize=%d\n",
*anymail,*newmail,*unreadmail,oldsize,newsize);
#endif
oldsize=newsize;
} else {
#endif /* SUPPORT_MAILDIR */
fd = open (filename, O_RDONLY, 0);
if (fd < 0)
{
*anymail = 0;
*newmail = 0;
*unreadmail = 0;
newsize = 0;
}
else
{
fstat(fd, &st);
close(fd);
newsize = st.st_size;
if (newsize > 0)
*anymail = 1;
else
*anymail = 0;
if (st.st_mtime >= st.st_atime && newsize > 0)
*unreadmail = 1;
else
*unreadmail = 0;
if (newsize > oldsize && *unreadmail)
*newmail = 1;
else
*newmail = 0;
}
#ifdef SUPPORT_MAILDIR
}
#endif
oldsize = newsize;
}
/*---------------------------------------------------------------------------*/
syntax highlighted by Code2HTML, v. 0.9.1