static int minimum_priority;
static time_t last_scan_time;
static char *run_cmd = "mutt -f %s";
+static int simple_tab;
struct options {
int priority;
int hotkey;
int led;
int osd;
+ int unread_is_new;
};
struct option_node {
int scanning;
int seen;
time_t last_time;
+ time_t display_valid_until;
int last_size, last_pos;
int total, new, flagged;
int last_total, last_new, last_flagged;
int last_beep_new;
int force_refresh;
int snippet_is_new;
- char snippet[256];
+ char sender_snippet[128];
+ char subject_snippet[128];
};
static clist mboxes;
static clist osd_opts;
static void redraw_line(int i);
-static void rethink_display(void);
+static void rethink_display(int notify);
static void
add_pattern(char *patt)
o->hotkey = -1;
o->led = -1;
o->osd = -1;
+ o->unread_is_new = -1;
}
static void
MERGE(hotkey);
MERGE(led);
MERGE(osd);
+ MERGE(unread_is_new);
}
}
}
static void
-prepare_snippet(struct mbox *b, char *sender, char *subject)
+prepare_snippets(struct mbox *b, char *sender, char *subject)
{
+ char *pos, *term;
+
while (*sender == ' ' || *sender == '\t')
sender++;
while (*subject == ' ' || *subject == '\t')
subject++;
- char *pos = b->snippet;
- char *term = b->snippet + sizeof(b->snippet) - 1;
+ pos = b->sender_snippet;
+ term = pos + sizeof(b->sender_snippet) - 1;
if (sender[0] && (b->o.sender_mbox || b->o.sender_personal))
- {
- add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
- add_snippet(&pos, term, ": ");
- }
+ add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal);
+ else
+ *pos = 0;
+
+ pos = b->subject_snippet;
+ term = pos + sizeof(b->subject_snippet) - 1;
if (subject[0])
add_subject_snippet(&pos, term, subject);
else
- add_snippet(&pos, term, "No subject");
+ add_snippet_raw(&pos, term, "No subject");
+}
+
+static void
+build_snippet(char *buf, char *term, struct mbox *b)
+{
+ if (b->sender_snippet[0])
+ {
+ add_snippet(&buf, term, b->sender_snippet);
+ add_snippet_raw(&buf, term, ": ");
+ }
+ add_snippet(&buf, term, b->subject_snippet);
}
static int mb_fd, mb_pos;
return 1;
}
+static void
+mb_skip(int len)
+{
+ while (len)
+ {
+ int avail = mb_end - mb_cc;
+ if (!avail)
+ {
+ if (mb_ll_get() < 0)
+ return;
+ len--;
+ }
+ else
+ {
+ int next = (avail < len) ? avail : len;
+ len -= next;
+ mb_cc += next;
+ }
+ }
+}
+
static void
scan_mbox(struct mbox *b, struct stat *st)
{
while ((c = mb_get()) >= 0 && c != '\n')
;
+ int content_length = -1;
int new = 1;
int flagged = 0;
sender[0] = 0;
}
if (c == '\n')
{
+ if (!i)
+ break;
int fold = -1;
do
{
if (!buf[0])
break;
if (!strncasecmp(buf, "Status:", 7))
- new = 0;
+ {
+ if (!b->o.unread_is_new || strchr(buf + 7, 'R'))
+ new = 0;
+ }
else if (!strncasecmp(buf, "X-Status:", 9) && strchr(buf+9, 'F'))
flagged = 1;
else if (!strncasecmp(buf, "From:", 5))
strcpy(sender, buf+5);
else if (!strncasecmp(buf, "Subject:", 8))
strcpy(subject, buf+8);
+ else if (!strncasecmp(buf, "Content-Length:", 15))
+ content_length = atoi(buf + 15);
}
b->total++;
b->new++;
if (flagged)
b->flagged++;
+ if (debug_mode > 1)
+ debug("new=%d flagged=%d len=%d sender=<%s> subject=<%s>\n", new, flagged, content_length, sender, subject);
if (new || (flagged && !b->snippet_is_new))
{
b->snippet_is_new = new;
- prepare_snippet(b, sender, subject);
+ prepare_snippets(b, sender, subject);
}
+ if (content_length >= 0)
+ mb_skip(content_length);
+
int ct = 1;
while (from[ct])
{
}
static void
-scan(void)
+scan(int notify)
{
- debug("Searching for mailboxes...\n");
+ debug("Searching for mailboxes (notify=%d)...\n", notify);
last_scan_time = time(NULL);
CLIST_FOR_EACH(struct pattern_node *, p, patterns)
{
}
}
- rethink_display();
+ rethink_display(0);
debug("Scanning mailboxes...\n");
CLIST_FOR_EACH(struct mbox *, b, mboxes)
redraw_line(b->index);
refresh();
}
+ else if (b->display_valid_until <= last_scan_time)
+ {
+ debug("not changed, but needs redraw\n");
+ redraw_line(b->index);
+ }
else
debug("not changed\n");
b->force_refresh = 0;
debug("Scan finished\n");
last_scan_time = time(NULL);
- rethink_display();
+ rethink_display(notify);
}
#ifdef CONFIG_X11
static Display *x11_dpy;
static unsigned leds_care, leds_have, leds_want;
-static unsigned osd_care, osd_have, osd_want;
+
static Atom osd_pty;
+static unsigned osd_care;
+#define OSD_MSG_SIZE 1024
+static char osd_last_msg[OSD_MSG_SIZE];
+static time_t osd_last_time;
static void
x11_init(void)
if (!n->key[0])
seen_msg = 1;
if (!seen_msg)
- add_osd_opt("=You have new mail");
+ {
+ add_osd_opt("=%40f");
+ add_osd_opt("=%40s");
+ add_osd_opt("=");
+ add_osd_opt("=(and %m more)");
+ }
}
leds_have = ~0U;
}
static void
-sync_osd(void)
+rethink_leds(void)
+{
+ if (!leds_care || !x11_dpy)
+ return;
+
+ leds_want = 0;
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ if (b->o.led > 0 && b->new)
+ leds_want |= (1 << b->o.led);
+ sync_leds();
+}
+
+struct osd_params {
+ struct mbox *mbox;
+ int total_new;
+};
+
+static int
+format_osd_string(char *dest, char *src, struct osd_params *par)
+{
+ char *stop = dest + OSD_MSG_SIZE - 1;
+ char numbuf[16];
+
+ while (*src && dest < stop)
+ {
+ if (*src == '%')
+ {
+ src++;
+ int size = 0;
+ while (*src >= '0' && *src <= '9')
+ size = 10*size + *src++ - '0';
+ if (!size || size > stop-dest)
+ size = dest-stop;
+
+ int spec = *src++;
+ if (!spec)
+ break;
+
+ char *arg = numbuf;
+ switch (spec)
+ {
+ case 'f':
+ arg = par->mbox->sender_snippet;
+ break;
+ case 's':
+ arg = par->mbox->subject_snippet;
+ break;
+ case 'n':
+ snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
+ break;
+ case 'm':
+ if (par->total_new < 2)
+ return 0;
+ snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
+ break;
+ case '%':
+ arg = "%";
+ break;
+ default:
+ arg = "???";
+ break;
+ }
+
+ while (*arg && size)
+ {
+ *dest++ = *arg++;
+ size--;
+ }
+ }
+ else
+ *dest++ = *src++;
+ }
+ *dest = 0;
+ return 1;
+}
+
+static void
+format_osd(char *msg, struct osd_params *par)
{
- if (!osd_want || !allow_osd)
+ if (!par->mbox)
{
- osd_have = 0;
+ msg[0] = 0;
return;
}
- if (osd_have)
- return;
- debug("OSD: Displaying\n");
- osd_have = 1;
- char msg[1024];
unsigned pos = 0;
+ unsigned have_text = 0;
CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
{
- pos += snprintf(msg+pos, sizeof(msg)-pos-1, "%s:%s\n", n->key, n->val);
- if (pos > sizeof(msg)-1)
+ char buf[OSD_MSG_SIZE];
+ if (!format_osd_string(buf, n->val, par))
+ continue;
+ if (!n->key[0] && buf[0])
+ have_text = 1;
+ pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf);
+ if (pos > OSD_MSG_SIZE-1)
{
pos = sprintf(msg, "OSD message too long!\n");
break;
}
}
- msg[pos++] = '\n';
-
- XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) msg, pos);
- XFlush(x11_dpy);
+ if (have_text)
+ msg[pos++] = '\n';
+ else
+ pos = 0;
+ msg[pos] = 0;
}
static void
-rethink_leds(void)
+debug_osd_msg(char *msg)
{
- if (!x11_dpy)
+ if (!debug_mode)
return;
+ fprintf(stderr, "OSD: <");
+ while (*msg)
+ {
+ fputc((*msg != '\n' ? *msg : '|'), stderr);
+ msg++;
+ }
+ fprintf(stderr, ">\n");
+}
- leds_want = 0;
- osd_want = 0;
+static void
+rethink_osd(int notify)
+{
+ if (!osd_care || !x11_dpy || !allow_osd)
+ {
+ osd_last_msg[0] = 0;
+ return;
+ }
+
+ struct osd_params p = { .mbox = NULL, .total_new = 0 };
CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ if (b->o.osd > 0)
+ {
+ p.total_new += b->new;
+ if (b->new && b->last_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
+ p.mbox = b;
+ }
+
+ char new_msg[OSD_MSG_SIZE];
+ format_osd(new_msg, &p);
+ debug_osd_msg(new_msg);
+ if (strcmp(new_msg, osd_last_msg))
{
- if (b->o.led > 0 && b->new)
- leds_want |= (1 << b->o.led);
- if (b->o.osd > 0 && b->new)
- osd_want = 1;
+ strcpy(osd_last_msg, new_msg);
+ if (notify && new_msg[0])
+ {
+ debug("OSD: Sending to daemon\n");
+ XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg));
+ XFlush(x11_dpy);
+ }
+ else
+ debug("OSD: No changes\n");
+ osd_last_time = time(NULL);
}
- sync_leds();
- sync_osd();
}
static void
static void x11_init(void) { }
static void rethink_leds(void) { }
+static void rethink_osd(int notify UNUSED) { }
static void x11_cleanup(void) { }
#endif
int hi = b->o.highlight;
unsigned namepos = 0;
unsigned namelen = strlen(b->name);
+ int valid = 3600;
attrset(attrs[cc][hi][M_IDLE]);
if (b->o.hotkey)
if (age < 0)
age = 0;
if (age < 3600)
- printw("%2d min ", age/60);
+ {
+ printw("%2d min ", age/60);
+ valid = 60;
+ }
else if (age < 86400)
- printw("%2d hrs ", age/3600);
+ printw("%2d hr%c ", age/3600, (age >= 7200 ? 's' : ' '));
else
printw(" ");
snip = 1;
attrset(attrs[cc][0][M_FLAG]); /* We avoid the highlight intentionally */
snip = 1;
}
- if (snip && b->o.snippets && b->snippet[0])
+ if (snip && b->o.snippets)
{
int xx, yy;
getyx(stdscr, yy, xx);
int remains = COLS-1-xx;
- if (remains > 2)
+ (void) yy;
+
+ char snip[256];
+ build_snippet(snip, snip + sizeof(snip) - 1, b);
+
+ if (snip[0] && remains > 2)
{
#ifdef CONFIG_WIDE_CURSES
- size_t len = strlen(b->snippet)+1;
- wchar_t snip[len];
- mbstowcs(snip, b->snippet, len);
- addnwstr(snip, remains);
+ size_t len = strlen(snip)+1;
+ wchar_t snip2[len];
+ mbstowcs(snip2, snip, len);
+ addnwstr(snip2, remains);
#else
- printw("%-.*s", remains, b->snippet);
+ printw("%-.*s", remains, snip);
#endif
}
}
}
+ b->display_valid_until = last_scan_time + valid;
}
attrset(attrs[0][0][M_IDLE]);
clrtoeol();
}
static void
-rethink_display(void)
+rethink_display(int notify)
{
int i = 0;
int changed = 0;
refresh();
}
rethink_leds();
- if (beeeep && allow_bells)
+ rethink_osd(notify);
+ if (beeeep && allow_bells && notify)
beep();
}
}
static void
-scan_and_redraw(void)
+scan_and_redraw(int notify)
{
print_status("Busy...");
- scan();
+ scan(notify);
print_status(NULL);
}
do
{
struct mbox *b = mbox_array[i];
- if (b->new && b->o.priority > bestp)
+ if (simple_tab)
{
- besti = i;
- bestp = b->o.priority;
+ if (b->new)
+ {
+ besti = i;
+ break;
+ }
+ }
+ else
+ {
+ if (b->new && b->o.priority > bestp)
+ {
+ besti = i;
+ bestp = b->o.priority;
+ }
}
i = (i+step) % cursor_max;
}
redraw_all();
refresh();
b->force_refresh = 1;
- scan_and_redraw();
+ scan_and_redraw(0);
}
static void
}
else
{
- print_status("");
+ print_status(NULL);
is_active = 0;
is_pos = 0;
redraw_line(cursor_at);
-o <opts>\t\tSet default options for all mailboxes\n\
-p <pri>\t\tSet minimum priority to show\n\
-s <key>=<val>\t\tSet on-screen display options (consult OSDD docs)\n\
+-t\t\t\tLet TAB select the next mailbox with new mail, no matter what priority it has\n\
\n\
Mailbox options (set with `-o', use upper case to negate):\n\
0-9\t\t\tSet mailbox priority (0=default)\n\
h\t\t\tHide from display\n\
l<led>\t\t\tLight a keyboard led (1-9) if running on X display\n\
m\t\t\tShow mailbox name of the sender\n\
+o\t\t\tCount old, but unread messages as new\n\
p\t\t\tShow personal info (full name) of the sender\n\
s\t\t\tShow message snippets\n\
t\t\t\tHighlight the entry\n\
case 'm':
o->sender_mbox = value;
break;
+ case 'o':
+ o->unread_is_new = value;
+ break;
case 'p':
o->sender_personal = value;
break;
clist_init(&osd_opts);
int c;
- while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
+ while ((c = getopt(argc, argv, "c:dim:o:p:s:t")) >= 0)
switch (c)
{
case 'c':
case 's':
add_osd_opt(optarg);
break;
+ case 't':
+ simple_tab = 1;
+ break;
default:
usage();
}
charset_init();
term_init();
x11_init();
- scan_and_redraw();
+ scan_and_redraw(0);
next_active(0, 1);
int should_exit = 0;
{
time_t now = time(NULL);
int remains = last_scan_time + check_interval - now;
- if (remains <= 0 || force_refresh)
- scan_and_redraw();
+ if (force_refresh)
+ scan_and_redraw(0);
+ if (remains <= 0)
+ scan_and_redraw(1);
else
{
remains *= 10;
if (ch >= '0' && ch <= '9')
{
minimum_priority = ch - '0';
- scan_and_redraw();
+ scan_and_redraw(0);
}
else
debug("Pressed unknown key %d\n", ch);