/* * WMMail - Window Maker Mail * * Copyright (c) 1996, 1997, 1998 Per Liden * Copyright (c) 1997, 1998 Bryan Chan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * wmmail.c: essential routines * * $Id: wmmail.c,v 1.1 2000/07/02 20:38:01 bryan.chan Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "wmmail.h" #include "wmutil.h" #include "properties.h" #include "std_icons.h" int end_of_cycle = False; #ifdef MBOX_SUPPORT # include "mbox.h" #endif #ifdef MH_SUPPORT # include "mh.h" #endif #ifdef MAILDIR_SUPPORT # include "maildir.h" #endif #ifdef POP3_SUPPORT # include "pop3.h" #endif #ifdef IMAP_SUPPORT # include "imap.h" #endif /* function prototypes */ int main(int, char **); void help(void); void read_configuration_data(void); void initialize(int, char **); void create_appicon(void); void redraw_appicon(void); void animate(XtPointer, XtIntervalId *); Pixel get_pixel_by_color(char *); void update_status(XtPointer, XtIntervalId *); void handle_expose(Widget, XtPointer, XEvent *); void handle_button(Widget, XtPointer, XEvent *); int main(int argc, char **argv) { initialize(argc, argv); create_appicon(); update_status(NULL, NULL); animate(NULL, NULL); XtAppMainLoop(wmmail_context); return 0; } void help(void) { fprintf(stderr, "WMMail version %s%s by Bryan Chan (bryan.chan@utoronto.ca)\n" "based on asmail version 0.50, by Per Liden (per@rsn.hk-r.se)\n" "\n" "usage: \t%s [options ...] []\n" "\n" ":\tfull path of defaults domain to use\n" "\n" "options:\t-help, -h display this message\n" " \t-quiet, -q suppress all error messages\n" " \t-swallowed, -s show window as well as appicon;\n" " \t good for swallowing with AfterStep\n" "\n", VERSION, #ifdef DEBUG "(with debug support),", #else ",", #endif app_name); exit(1); } void read_configuration_data(void) { char *domain; if (user_specified_domain) { if (!parse_gnustep_domain(user_specified_domain)) exit(-1); } else if (domain = find_resource(WMMAIL_CLASS, "Defaults")) { if (!parse_gnustep_domain(domain)) exit(-1); } else croak("cannot find any configuration data, using default values..."); } /* SIGCHLD handler */ void sig_chld(int signo) { waitpid((pid_t) -1, NULL, WNOHANG); signal(SIGCHLD, sig_chld); } /* handle command line options, open the configuration file for reading */ void initialize(int argc, char **argv) { /* determine how this program was invoked */ if ((char *) rindex(argv[0], '/') != NULL) app_name = (char *) rindex(argv[0], '/') + 1; else app_name = argv[0]; wmmail_widget = XtAppInitialize(&wmmail_context, WMMAIL_CLASS, (XrmOptionDescRec *) NULL, 0, &argc, argv, (String *) NULL, (ArgList) NULL, 0); disp = XtDisplay(wmmail_widget); root = RootWindow(disp, DefaultScreen(disp)); wmmail_font = XLoadFont(disp, DEFAULT_FONT); if (argc > 1) { int i; for (i = 1; i < argc; i++) if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h")) { help(); exit(0); } else if (!strcmp(argv[i], "-quiet") || !strcmp(argv[i], "-q")) { keep_quiet = True; } else if (!strcmp(argv[i], "-swallowed") || !strcmp(argv[i], "-s")) { use_appicon_only = False; } else if (argv[i][0] == '-') { croak("no such option '%s'", argv[i]); exit(-1); } else if (user_specified_domain != NULL) { croak("cannot use more than one defaults domain"); exit(-1); } else user_specified_domain = wstrdup(argv[i]); } read_configuration_data(); /* if we're cycling text, set up for animation of mailbox names */ if (display_names) { int i; for (i = 0; i < 3; i++) { animations[i]->text_cycle = 0; } } /* if no mailbox is specified, assume $MAIL is a UNIX mbox */ #ifdef MBOX_SUPPORT if (mailbox_list == NULL && getenv("MAIL") != NULL) { proplist_t key, path; Mailbox *mailbox; char *mailbox_path; struct stat t; mailbox_path = getenv("MAIL"); if (mailbox_path && !stat(mailbox_path, &t)) { if (S_ISREG(t.st_mode)) { mailbox = (Mailbox *) wmalloc(sizeof(Mailbox)); mailbox->name = wstrdup("Inbox"); mailbox->type = TYPE_MBOX; mailbox->status = NO_MAIL; mailbox->total_mail_count = 0; mailbox->new_mail_count = 0; mailbox->update_interval = DEFAULT_INTERVAL; mailbox->last_update = 0; mailbox->size = t.st_size; key = PLMakeString("Path"); path = PLMakeString(mailbox_path); mailbox->options = PLMakeDictionaryFromEntries(key, path, NULL); PLRelease(key); PLRelease(path); mailbox_list = list_cons(mailbox, mailbox_list); } else { croak("default mailbox %s not a UNIX mbox; aborting", mailbox_path); exit(-1); } } else { croak("cannot stat default mailbox %s", mailbox_path ? mailbox_path : ""); exit(-1); } } else #endif if (mailbox_list == NULL) { croak("no usable mailbox (already tried $MAIL)"); exit(-1); } /* zombie children are dealt with in the SIG_CHLD handler */ signal(SIGCHLD, sig_chld); } void create_appicon(void) { Arg args[4]; XGCValues gc_values; load_std_icons(); wmmail_appicon = XtCreateWidget("AppIcon", topLevelShellWidgetClass, wmmail_widget, NULL, 0); XtAddEventHandler(wmmail_appicon, ExposureMask, False, (XtEventHandler) handle_expose, NULL); XtAddEventHandler(wmmail_appicon, ButtonPressMask, False, (XtEventHandler) handle_button, NULL); XtResizeWidget(wmmail_appicon, animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height, 1); XtRealizeWidget(wmmail_appicon); XtSetArg(args[0], XtNiconName, app_name); XtSetArg(args[1], XtNiconWindow, XtWindow(wmmail_appicon)); if (!use_appicon_only) XtSetArg(args[2], XtNinitialState, NormalState); else XtSetArg(args[2], XtNinitialState, WithdrawnState); XtSetValues(wmmail_widget, args, 3); if (!use_appicon_only) { XtAddEventHandler(wmmail_widget, ExposureMask, False, (XtEventHandler) handle_expose, NULL); XtAddEventHandler(wmmail_widget, ButtonPressMask, False, (XtEventHandler) handle_button, NULL); } XtResizeWidget(wmmail_widget, animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height, 1); XtRealizeWidget(wmmail_widget); XtMapWidget(wmmail_widget); shape_mask = XCreatePixmap(disp, XtWindow(wmmail_widget), animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height, 1); shape_mask_gc = XCreateGC(disp, shape_mask, 0, &gc_values); XSetGraphicsExposures(disp, shape_mask_gc, False); XSetBackground(disp, shape_mask_gc, 0); XSetForeground(disp, shape_mask_gc, 1); wmmail_gc = XCreateGC(disp, root, 0, &gc_values); XSetGraphicsExposures(disp, wmmail_gc, False); XSetBackground(disp, wmmail_gc, get_pixel_by_color("black")); XSetForeground(disp, wmmail_gc, get_pixel_by_color(wmmail_color)); redraw_appicon(); } void redraw_appicon(void) { char buf[30]; XTextItem num_of_msg; /* create shape mask from the mask of the XPM animation frame */ XFillRectangle(disp, shape_mask, shape_mask_gc, 0, 0, animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height); XCopyArea(disp, animations[wmmail_status]->mask, shape_mask, shape_mask_gc, 0, 0, animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height, 0, 0); /* generate NumOfMsg if NumOfMsgMode is used */ if (num_of_msg_mode != SHOW_NONE || display_names) { Mailbox *mailbox; int i, j, sum_new_mail, sum_total_mail; static char *box_name = NULL; static int box_new = 0; static int box_total = 0; j = animations[wmmail_status]->text_cycle; if (j != -1) { box_name = NULL; } for (i = 0, sum_new_mail = 0, sum_total_mail = 0; i < mailbox_count; i++) { mailbox = list_nth(i, mailbox_list); sum_new_mail += mailbox->new_mail_count; sum_total_mail += mailbox->total_mail_count; if (j > -1 && mailbox->status == NEW_MAIL) { j--; if (j <= 0) { box_name = mailbox->name; box_new = mailbox->new_mail_count; box_total = mailbox->total_mail_count; #ifdef DEBUG croak("box_name: %s box_new: %d box_total: %d", box_name, box_new, box_total); #endif } } } if (j >= 0) { end_of_cycle = True; box_name = NULL; box_new = box_total = 0; } if (display_names && box_name) { switch(num_of_msg_mode) { case SHOW_NEW_OVER_TOTAL: sprintf(buf, "%s %d/%d", box_name, box_new, box_total); break; case SHOW_NEW_ONLY: sprintf(buf, "%s %d", box_name, box_new); break; case SHOW_TOTAL_ONLY: sprintf(buf, "%s %d", box_name, box_total); break; case SHOW_NONE: sprintf(buf, "%s", box_name); break; } } else { switch(num_of_msg_mode) { case SHOW_NEW_OVER_TOTAL: sprintf(buf, "%d/%d", sum_new_mail, sum_total_mail); break; case SHOW_NEW_ONLY: sprintf(buf, "%d", sum_new_mail); break; case SHOW_TOTAL_ONLY: sprintf(buf, "%d", sum_total_mail); break; } } num_of_msg.chars = buf; num_of_msg.nchars = strlen(buf); num_of_msg.delta = 1; num_of_msg.font = wmmail_font; /* add the text to the shape mask */ XDrawText(disp, shape_mask, shape_mask_gc, num_of_msg_x, num_of_msg_y, &num_of_msg, 1); XDrawText(disp, shape_mask, shape_mask_gc, num_of_msg_x, num_of_msg_y, &num_of_msg, 1); } /* apply the shape mask */ XShapeCombineMask(disp, XtWindow(wmmail_appicon), ShapeBounding, 0, 0, shape_mask, ShapeSet); if (!use_appicon_only) XShapeCombineMask(XtDisplay(wmmail_widget), XtWindow(wmmail_widget), ShapeBounding, 0, 0, shape_mask, ShapeSet); /* draw XPM animation frame */ XCopyArea(disp, animations[wmmail_status]->pixmap, XtWindow(wmmail_appicon), wmmail_gc, 0, 0, animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height, 0, 0); if (!use_appicon_only) XCopyArea(XtDisplay(wmmail_widget), animations[wmmail_status]->pixmap, XtWindow(wmmail_widget), wmmail_gc, 0, 0, animations[wmmail_status]->attributes.width, animations[wmmail_status]->attributes.height, 0, 0); /* draw NumOfMsg */ if (num_of_msg_mode != SHOW_NONE) { XDrawText(disp, XtWindow(wmmail_appicon), wmmail_gc, num_of_msg_x, num_of_msg_y, &num_of_msg, 1); if (!use_appicon_only) XDrawText(disp, XtWindow(wmmail_widget), wmmail_gc, num_of_msg_x, num_of_msg_y, &num_of_msg, 1); } XFlush(disp); } void animate(XtPointer XtP, XtIntervalId *XtI) { animations[wmmail_status] = animations[wmmail_status]->next; if (animations[wmmail_status]->text_cycle != -1) { if (end_of_cycle) { animations[wmmail_status]->text_cycle = 0; end_of_cycle = False; } else { animations[wmmail_status]->text_cycle++; } } redraw_appicon(); XtAppAddTimeOut(wmmail_context, anim_speed[wmmail_status], animate, NULL); } Pixel get_pixel_by_color(char *color_name) { XColor color; XWindowAttributes attributes; XGetWindowAttributes(disp, root, &attributes); color.pixel = 0; if (!XParseColor(disp, attributes.colormap, color_name, &color)) croak("cannot parse color '%s', using black", color_name); else if (!XAllocColor(disp, attributes.colormap, &color)) croak("cannot allocate color '%s', using black", color_name); return color.pixel; } void update_status(XtPointer XtP, XtIntervalId * XtI) { Mailbox *mailbox; static int update_in_progress = False; int i, ret; int new_status = NO_MAIL; int beep = False, redraw = False, run = False, next_update = -1; /* time to next update in seconds */ if (!update_in_progress) { update_in_progress = True; mailbox_count = list_length(mailbox_list); for (i = 0; i < mailbox_count; i++) { mailbox = list_nth(i, mailbox_list); if (mailbox->last_update + mailbox->update_interval > time(NULL)) { /* don't check this mailbox if it isn't time yet */ ret = True; } else { #ifdef DEBUG croak("checking mailbox %s", mailbox->name); #endif if (mailbox->execute_on_update) exec_command(mailbox->execute_on_update); #ifdef MBOX_SUPPORT if (IS_MBOX(mailbox)) ret = MBOX_check(mailbox, &beep, &redraw, &run); else #endif #ifdef MH_SUPPORT if (IS_MH(mailbox)) ret = MH_check(mailbox, &beep, &redraw, &run); else #endif #ifdef MAILDIR_SUPPORT if (IS_MAILDIR(mailbox)) ret = MAILDIR_check(mailbox, &beep, &redraw, &run); else #endif #ifdef POP3_SUPPORT if (IS_POP3(mailbox)) ret = POP3_check(mailbox, &beep, &redraw, &run); else #endif #ifdef IMAP_SUPPORT if (IS_IMAP(mailbox)) ret = IMAP_check(mailbox, &beep, &redraw, &run); else #endif /* ignore anything that we cannot handle; bug in properties.c? */ continue; } /* delete mailbox if it has become rotten */ if (!ret) { mailbox_list = list_remove_elem(mailbox_list, mailbox); wfree(mailbox->execute_on_update); wfree(mailbox->name); wfree(mailbox); mailbox_count--; i--; #ifdef DEBUG croak("deleting mailbox %d", i + 1); croak("mailbox count: %d", mailbox_count); #endif } else { int tmp = mailbox->last_update + mailbox->update_interval; if (mailbox->status > new_status) new_status = mailbox->status; if ( (next_update == -1) || (time(NULL) < tmp && time(NULL) + next_update > tmp) ) next_update = tmp - time(NULL); } } if (mailbox_count == 0) { croak("no functional mailbox left to monitor"); exit(-1); } wmmail_status = new_status; update_in_progress = False; } if (run && exec_on_new != NULL) exec_command(exec_on_new); if (beep && use_beep) XBell(disp, 0); if (redraw) redraw_appicon(); #ifdef DEBUG croak("next update: %d", next_update); #endif if (next_update < 0) next_update = DEFAULT_INTERVAL; XtAppAddTimeOut(wmmail_context, next_update * 1000, update_status, NULL); } void handle_expose(Widget w, XtPointer p, XEvent *e) { if (e->xexpose.count == 0) redraw_appicon(); } void handle_button(Widget w, XtPointer p, XEvent *e) { static Time last_left_mouse_click = -1; static Time last_middle_mouse_click = -1; if (e->xbutton.button == LEFT_BUTTON && exec_on_click != NULL) { if (last_left_mouse_click > 0 && e->xbutton.time - last_left_mouse_click <= double_click_time) { last_left_mouse_click = -1; exec_command(exec_on_click); } last_left_mouse_click = e->xbutton.time; } else if (e->xbutton.button == MIDDLE_BUTTON) { if (last_middle_mouse_click > 0 && e->xbutton.time - last_middle_mouse_click <= double_click_time) { /* double-clicking with the middle button overrides mailbox status */ Mailbox *mailbox; int i; int new_status = NO_MAIL; for (i = 0; i < list_length(mailbox_list); i++) { mailbox = list_nth(i, mailbox_list); if (mailbox->status == NEW_MAIL) mailbox->status = OLD_MAIL; if (mailbox->status > new_status) new_status = mailbox->status; } if (wmmail_status != new_status) { wmmail_status = new_status; redraw_appicon(); } else wmmail_status = new_status; } last_middle_mouse_click = e->xbutton.time; } }