/* -*-c-*- */ /* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #include "libs/ftime.h" #include #include #include #include #include #include #include #include "libs/fvwmlib.h" #include "libs/FShape.h" #include "libs/Colorset.h" #include "libs/Module.h" #include "libs/Rectangles.h" #include "libs/FGettext.h" #include "libs/ColorUtils.h" #include "libs/Graphics.h" #include "libs/Parse.h" #include "libs/Strings.h" #include "Goodies.h" #include "FvwmTaskBar.h" #include "Mallocs.h" #include "minimail.xbm" #define MAILCHECK_DEFAULT 10 extern Display *dpy; extern Window Root, win; extern int Fvwm_fd[2]; extern int screen; extern ModuleArgs *module; extern int win_width, win_height, win_y, win_border, RowHeight, Midline; extern rectangle screen_g; extern Pixel back, fore; extern int colorset; extern GC blackgc, hilite, shadow, checkered; extern FlocaleWinString *FwinString; GC statusgc = 0; GC tipsgc = 0; FlocaleFont *FStatusFont; int stwin_width = 100, goodies_width = 0; int anymail, unreadmail, newmail, mailcleared = 0; int goodies_fontheight, clock_width; char *mailpath = NULL; char *clockfmt = NULL; char *datefmt = NULL; Bool do_display_clock = True; int BellVolume = DEFAULT_BELL_VOLUME; Pixmap mailpix = None; Pixmap wmailpix = None; Pixmap pmask = None; Pixmap pclip = None; Bool do_check_mail = True; Bool has_mailpath = True; int mailcheck_interval = MAILCHECK_DEFAULT; char *TipsFore = "black", *TipsBack = "LightYellow", *MailCmd = "Exec xterm -e mail"; int tipscolorset = -1; int IgnoreOldMail = False; int ShowTips = False; char *statusfont_string = NULL; int last_date = -1; Bool using_MailDir = False; void cool_get_inboxstatus(void); #define gray_width 8 #define gray_height 8 extern unsigned char gray_bits[]; /* x y w h tw th open type px py *text win */ TipStruct Tip = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, None }; static void CreateOrUpdateGoodyGC(void) { XGCValues gcval; unsigned long gcmask; Pixel pfore; Pixel pback; if (colorset >= 0) { pfore = Colorset[colorset].fg; pback = Colorset[colorset].bg; } else { pfore = fore; pback = back; } gcmask = GCForeground | GCBackground | GCGraphicsExposures; gcval.foreground = pfore; gcval.background = pback; gcval.graphics_exposures = False; if (FStatusFont->font != NULL) { gcval.font = FStatusFont->font->fid; gcmask = GCFont; } if (statusgc) XChangeGC(dpy, statusgc, gcmask, &gcval); else statusgc = fvwmlib_XCreateGC(dpy, win, gcmask, &gcval); if (do_check_mail) { if (mailpix) XFreePixmap(dpy, mailpix); mailpix = XCreatePixmapFromBitmapData( dpy, win, (char *)minimail_bits, minimail_width, minimail_height, pfore, pback, Pdepth); if (wmailpix) XFreePixmap(dpy, wmailpix); wmailpix = XCreatePixmapFromBitmapData( dpy, win, (char *)minimail_bits, minimail_width, minimail_height, PictureBlackPixel(), PictureWhitePixel(), Pdepth); goodies_width += minimail_width + 7; } else if (do_display_clock) { goodies_width += 3; } else { goodies_width += 0; } } Bool change_goody_colorset(int cset, Bool force) { if (cset < 0) { return False; } if (cset == tipscolorset && Tip.win != None) { char *s; if (Tip.text != NULL) { s = safestrdup(Tip.text); } else { s = NULL; } PopupTipWindow(Tip.px, Tip.py, s); if (s != NULL) { free(s); } } if (!force && cset != colorset) { return False; } CreateOrUpdateGoodyGC(); return True; } static char *goodyopts[] = { "BellVolume", "Mailbox", "Mailcheck", "ClockFormat", "StatusFont", "TipsFore", "TipsBack", "TipsColorset", "MailCommand", "IgnoreOldMail", "ShowTips", "DateFormat", "MailDir", NULL }; /* Parse 'goodies' specific resources */ Bool GoodiesParseConfig(char *tline) { char *rest; char *option; int i; option = tline + module->namelen+1; i = GetTokenIndex(option, goodyopts, -1, &rest); while (*rest && *rest != '\n' && isspace(*rest)) rest++; switch(i) { case 0: /* BellVolume */ BellVolume = atoi(rest); break; case 1: /* Mailbox */ if (strcasecmp(rest, "None") == 0) { has_mailpath = False; } else { int len; UpdateString(&mailpath, rest); len = strlen(mailpath); if (len > 0 && mailpath[len-1] == '\n') mailpath[len-1] = 0; has_mailpath = True; } do_check_mail = (has_mailpath && (mailcheck_interval > 0)); break; case 2: /* Mailcheck */ mailcheck_interval = MAILCHECK_DEFAULT; sscanf(rest, "%d", &mailcheck_interval); do_check_mail = (has_mailpath && (mailcheck_interval > 0)); break; case 3: /* ClockFormat */ UpdateString(&clockfmt, rest); do_display_clock = True; if (clockfmt && clockfmt[0] == '\0' && datefmt && datefmt[0] == '\0') { do_display_clock = False; } break; case 4: /* StatusFont */ CopyStringWithQuotes(&statusfont_string, rest); break; case 5: /* TipsFore */ CopyString(&TipsFore, rest); tipscolorset = -1; break; case 6: /* TipsBack */ CopyString(&TipsBack, rest); tipscolorset = -1; break; case 7: /* TipsColorset */ tipscolorset = -1; tipscolorset = atoi(rest); AllocColorset(tipscolorset); break; case 8: /* MailCommand */ CopyString(&MailCmd, rest); break; case 9: /* IgnoreOldMail */ IgnoreOldMail = True; break; case 10: /* ShowTips */ ShowTips = True; break; case 11: /* DateFormat */ UpdateString(&datefmt, rest); do_display_clock = True; if (clockfmt && clockfmt[0] == '\0' && datefmt && datefmt[0] == '\0') { do_display_clock = False; } break; case 12: /* MailDir */ using_MailDir = True; break; default: /* unknown option */ return False; } /* switch */ return True; } void LoadGoodiesFont(void) { if ((FStatusFont = FlocaleLoadFont(dpy, statusfont_string, module->name)) == NULL) { fprintf(stderr, "%s: Couldn't load font. Exiting!\n", module->name); exit(1); } goodies_fontheight = FStatusFont->height; } void InitGoodies(void) { struct passwd *pwent; char tmp[1024]; if (mailpath == NULL) { strcpy(tmp, DEFAULT_MAIL_PATH); pwent = getpwuid(getuid()); strcat(tmp, (char *) (pwent->pw_name)); UpdateString(&mailpath, tmp); } CreateOrUpdateGoodyGC(); if (clockfmt) { if (!do_display_clock) { clock_width = 0; } else { struct tm *tms; static time_t timer; static char str[24]; time(&timer); tms = localtime(&timer); strftime(str, 24, clockfmt, tms); clock_width = FlocaleTextWidth( FStatusFont, str, strlen(str)) + 4; } } else { clock_width = FlocaleTextWidth(FStatusFont, "XX:XX", 5) + 4; } goodies_width += clock_width; stwin_width = goodies_width; } void Draw3dBox(Window wn, int x, int y, int w, int h, XRectangle *bounding) { XRectangle r; if (bounding) { r.x = bounding->x; r.y = bounding->y; r.width = bounding->width; r.height = bounding->height; } else { r.x = x; r.y = y; r.width = w; r.height = h; } XClearArea(dpy, wn, r.x, r.y, r.width, r.height, False); XDrawLine(dpy, win, shadow, x, y, x+w-2, y); XDrawLine(dpy, win, shadow, x, y, x, y+h-2); XDrawLine(dpy, win, hilite, x, y+h-1, x+w-1, y+h-1); XDrawLine(dpy, win, hilite, x+w-1, y+h-1, x+w-1, y); } void DrawGoodies(XEvent *evp) { XRectangle rect; struct tm *tms; static char str[40]; static time_t timer; static int last_mail_check = -1; int x = win_width - stwin_width; int y = 0; int w = stwin_width; int h = RowHeight; Region t_region; if (goodies_width == 0) { return; } if (evp) { if (!frect_get_intersection( x, y, w, h, evp->xexpose.x, evp->xexpose.y, evp->xexpose.width, evp->xexpose.height, &rect)) { return; } } else { rect.x = x; rect.y = y; rect.width = w; rect.height = h; } time(&timer); tms = localtime(&timer); if (clockfmt) { strftime(str, 24, clockfmt, tms); if (str[0] == '0') str[0]=' '; } else { strftime(str, 15, "%R", tms); } Draw3dBox(win, x, y, w, h, &rect); t_region = XCreateRegion(); XUnionRectWithRegion (&rect, t_region, t_region); FwinString->win = win; FwinString->gc = statusgc; FwinString->str = str; FwinString->x = x + 4; FwinString->y = ((RowHeight - goodies_fontheight) >> 1) + FStatusFont->ascent; FwinString->flags.has_clip_region = True; FwinString->clip_region = t_region; if (colorset >= 0) { FwinString->colorset = &Colorset[colorset]; FwinString->flags.has_colorset = True; } else { FwinString->flags.has_colorset = False; } FlocaleDrawString(dpy, FStatusFont, FwinString, 0); FwinString->flags.has_clip_region = False; XDestroyRegion(t_region); if (!do_check_mail) { return; } if (timer - last_mail_check >= mailcheck_interval) { cool_get_inboxstatus(); last_mail_check = timer; if (newmail) XBell(dpy, BellVolume); } if (!mailcleared && (unreadmail || newmail)) { XCopyArea(dpy, wmailpix, win, statusgc, 0, 0, minimail_width, minimail_height, win_width - stwin_width + clock_width + 3, ((RowHeight - minimail_height) >> 1)); } else if (!IgnoreOldMail /*&& !mailcleared*/ && anymail) { XCopyArea(dpy, mailpix, win, statusgc, 0, 0, minimail_width, minimail_height, win_width - stwin_width + clock_width + 3, ((RowHeight - minimail_height) >> 1)); } if (Tip.open) { if (Tip.type == DATE_TIP) { if (tms->tm_mday != last_date) { /* reflect date change */ CreateDateWindow(); /* This automatically * deletes any old window */ } } last_date = tms->tm_mday; RedrawTipWindow(); } } int MouseInClock(int x, int y) { int clockl = win_width - stwin_width; int clockr = win_width - stwin_width + clock_width + (do_check_mail ? 2 : 3); return (x>clockl && x1 && y=maill && x1 && ytm_mday; PopupTipWindow(win_width, 0, str); } void CreateMailTipWindow(void) { const char *str; if (!anymail) { str = _("No new mail"); } else if (newmail || unreadmail) { str = _("You have new mail"); } else { str = _("You have mail"); } PopupTipWindow(win_width, 0, str); } void RedrawTipWindow(void) { /* FIXME: I am sure that we redraw too often */ if (Tip.text) { FwinString->win = Tip.win; FwinString->gc = tipsgc; FwinString->str = Tip.text; FwinString->x = 3; FwinString->y = 2 + FStatusFont->ascent; FwinString->flags.has_clip_region = False; if (tipscolorset >= 0) { FwinString->colorset = &Colorset[tipscolorset]; FwinString->flags.has_colorset = True; } else { FwinString->flags.has_colorset = False; } if (FftSupport && FStatusFont->fftf.fftfont != NULL) { XClearArea(dpy, Tip.win, 0, 0, Tip.w, Tip.h, False); } FlocaleDrawString(dpy, FStatusFont, FwinString, 0); XRaiseWindow(dpy, Tip.win); } } void PopupTipWindow(int px, int py, const char *text) { int newx, newy; Window child; Tip.px = px; Tip.py = py; if (!ShowTips) return; if (Tip.win != None) DestroyTipWindow(); Tip.tw = FlocaleTextWidth(FStatusFont, (char *)text, strlen(text)) + 6; Tip.th = FStatusFont->height + 4; XTranslateCoordinates(dpy, win, Root, px, py, &newx, &newy, &child); Tip.x = newx; /* if (win_y == win_border) */ if (win_y < Midline) Tip.y = newy + RowHeight; else Tip.y = newy - Tip.th -2; Tip.w = Tip.tw; Tip.h = Tip.th; if (Tip.x + Tip.tw + 4 > screen_g.x + screen_g.width - 5) Tip.x = screen_g.x + screen_g.width - Tip.tw - 9; if (Tip.x < screen_g.x + 5) Tip.x = screen_g.x + 5; UpdateString(&Tip.text, text); CreateTipWindow(Tip.x, Tip.y, Tip.w, Tip.h); if (Tip.open) XMapRaised(dpy, Tip.win); } void ShowTipWindow(int open) { if (!ShowTips) return; if (open) { if (Tip.win != None) { XMapRaised(dpy, Tip.win); RedrawTipWindow(); } } else { XUnmapWindow(dpy, Tip.win); } Tip.open = open; } void CreateTipWindow(int x, int y, int w, int h) { unsigned long gcmask; unsigned long winattrmask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap |CWSaveUnder | CWOverrideRedirect; XSetWindowAttributes winattr; GC cgc = None; GC gc0 = None; GC gc1 = None; XGCValues gcval; Pixmap pchk = None; Pixel tip_fore; Pixel tip_back; colorset_t *cset = NULL; if (tipscolorset >= 0) { cset = &Colorset[tipscolorset]; tip_fore = cset->fg; tip_back = cset->bg; } else { tip_fore = GetColor(TipsFore); tip_back = GetColor(TipsBack); } winattr.background_pixel = tip_back; winattr.border_pixel = PictureBlackPixel(); winattr.colormap = Pcmap; winattr.override_redirect = True; winattr.save_under = True; winattr.event_mask = ExposureMask; Tip.win = XCreateWindow(dpy, Root, x, y, w+4, h+4, 0, Pdepth, InputOutput, Pvisual, winattrmask, &winattr); gcmask = GCForeground | GCBackground | GCGraphicsExposures; gcval.graphics_exposures = False; gcval.foreground = tip_fore; gcval.background = tip_back; if (FStatusFont->font != NULL) { gcval.font = FStatusFont->font->fid; gcmask |= GCFont; } if (tipsgc) XChangeGC(dpy, tipsgc, gcmask, &gcval); else tipsgc = fvwmlib_XCreateGC(dpy, Tip.win, gcmask, &gcval); pmask = XCreatePixmap(dpy, Tip.win, w+4, h+4, 1); pclip = XCreatePixmap(dpy, Tip.win, w+4, h+4, 1); gcmask = GCForeground | GCBackground | GCFillStyle | GCStipple | GCGraphicsExposures; gcval.foreground = 1; gcval.background = 0; gcval.fill_style = FillStippled; if (pchk == None) pchk = XCreatePixmapFromBitmapData(dpy, Tip.win, (char *)gray_bits, gray_width, gray_height, 1, 0, 1); gcval.stipple = pchk; cgc = fvwmlib_XCreateGC(dpy, pmask, gcmask, &gcval); gcmask = GCForeground | GCBackground | GCGraphicsExposures | GCFillStyle; gcval.graphics_exposures = False; gcval.fill_style = FillSolid; gcval.foreground = 0; gcval.background = 0; gc0 = fvwmlib_XCreateGC(dpy, pmask, gcmask, &gcval); gcval.foreground = 1; gcval.background = 1; gc1 = fvwmlib_XCreateGC(dpy, pmask, gcmask, &gcval); XFillRectangle(dpy, pmask, gc0, 0, 0, w+4, h+4); XFillRectangle(dpy, pmask, cgc, 3, 3, w+4, h+4); XFillRectangle(dpy, pmask, gc1, 0, 0, w+1, h+1); XFillRectangle(dpy, pclip, gc0, 0, 0, w+4, h+4); XFillRectangle(dpy, pclip, gc1, 1, 1, w-1, h-1); if (FShapesSupported) { FShapeCombineMask(dpy, Tip.win, FShapeBounding, 0, 0, pmask, FShapeSet); FShapeCombineMask(dpy, Tip.win, FShapeClip, 0, 0, pclip, FShapeSet); } if (tipscolorset >= 0 && (cset->pixmap || cset->shape_mask)) { SetWindowBackground( dpy, Tip.win, w + 4, h + 4, cset, Pdepth, tipsgc, False); } XFreeGC(dpy, gc0); XFreeGC(dpy, gc1); XFreeGC(dpy, cgc); XFreePixmap(dpy, pchk); } void DestroyTipWindow(void) { XFreePixmap(dpy, pmask); XFreePixmap(dpy, pclip); XDestroyWindow(dpy, Tip.win); if (Tip.text) { free(Tip.text); Tip.text = NULL; } Tip.win = None; } /*-----------------------------------------------------*/ /* Get file modification time */ /* (based on the code of 'coolmail' By Byron C. Darrah */ /*-----------------------------------------------------*/ void cool_get_inboxstatus_mbox(void) { static off_t oldsize = 0; off_t newsize; struct stat st; int fd; fd = open (mailpath, 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; mailcleared = 0; } else newmail = 0; } oldsize = newsize; } static int f_scandir(const char *dir, struct dirent *** namelist) { DIR * f_dir; struct dirent * f_temp; /* 10 is just an arbituary number */ int f_count = 0, namelist_size = 10; void * r_ret; f_dir = opendir(dir); if (f_dir == NULL) return -1; namelist[0] = (struct dirent **)safemalloc(sizeof(struct dirent *) * 10); do { f_temp = readdir(f_dir); if (f_temp == NULL) break; else if (strcmp(f_temp->d_name, ".") != 0 && strcmp(f_temp->d_name, "..") != 0) { if (f_count > namelist_size) { r_ret = saferealloc( (void *)(namelist[0]), sizeof(struct dirent) * (f_count +10)); namelist[0] = (struct dirent **)r_ret; namelist_size = f_count + 10; } namelist[0][f_count] = (struct dirent *)safemalloc( sizeof(struct dirent)); memcpy( namelist[0][f_count], f_temp, sizeof(struct dirent)); f_count++; } } while (1); return f_count; } void cool_get_inboxstatus_maildir(void) { static int oldsize = 0; int newsize; struct dirent **newlist; struct dirent **curlist; char * alt_mailpath; int curent, newent; alt_mailpath = safemalloc(strlen(mailpath) + strlen("/new") + 1); strcpy(alt_mailpath, mailpath); strcat(alt_mailpath, "/new"); newent = f_scandir(alt_mailpath, &newlist); strcpy(alt_mailpath, mailpath); strcat(alt_mailpath, "/cur"); curent = f_scandir(alt_mailpath, &curlist); free(alt_mailpath); newsize = curent + newent; if (newent > 0) { anymail = 1; unreadmail = 1; } else if (curent > 0) { anymail = 1; unreadmail = 0; } else { anymail = 0; unreadmail = 0; } if (newsize > oldsize && unreadmail) { newmail = 1; mailcleared = 0; } else { newmail = 0; } oldsize = newsize; while(curent--) { free(curlist[curent]); } free(curlist); while(newent--) { free(newlist[newent]); } free(newlist); } void cool_get_inboxstatus(void) { if (using_MailDir) { cool_get_inboxstatus_maildir(); } else { cool_get_inboxstatus_mbox(); } } /*---------------------------------------------------------------------------*/ void HandleMailClick(XEvent event) { static Time lastclick = 0; if (event.xbutton.time - lastclick < 250) { SendText(Fvwm_fd, MailCmd, 0); } lastclick = event.xbutton.time; mailcleared = 1; }