#include "charset.h"
static int check_interval = 30;
+static int osd_repeat_interval = 0; // 0 is disable
static int force_refresh;
static int allow_bells = 1;
static int allow_osd = 1;
static int minimum_priority;
static time_t last_scan_time;
+static time_t last_osd_repeat_time;
static char *run_cmd = "mutt -f %s";
static int simple_tab;
int hotkey;
int led;
int osd;
+ int osd_repeat;
+ int osd_list;
int unread_is_new;
int sort_order;
};
static struct options global_options = {
.sender_personal = 1,
.sort_order = 1000,
+ .osd_repeat = 1,
+ .osd_list = 1,
};
#define MDIR_MAX_NAME_LEN 128
char sender_snippet[128];
char subject_snippet[128];
char mdir_best[MDIR_MAX_NAME_LEN];
+ int osd_repeat_last_total, osd_repeat_last_new, osd_repeat_last_flagged;
+ char osd_repeat_last_sender_snippet[128];
+ char osd_repeat_last_subject_snippet[128];
};
static clist mboxes;
};
static clist osd_opts;
+static clist osd_repeat_opts;
+static char *osd_mbox_opt="%a";
+static char *osd_mbox_opt_separator=" ";
static void redraw_line(int i);
static void rethink_display(int notify);
o->hotkey = -1;
o->led = -1;
o->osd = -1;
+ o->osd_repeat = -1;
+ o->osd_list = -1;
o->unread_is_new = -1;
o->sort_order = -1;
}
MERGE(hotkey);
MERGE(led);
MERGE(osd);
+ MERGE(osd_repeat);
+ MERGE(osd_list);
MERGE(unread_is_new);
MERGE(sort_order);
}
}
static void
-add_osd_opt(char *arg)
+add_osd_opt(char *arg, struct clist *opts)
{
struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
strcpy(n->key, arg);
if (!n->val)
die("Malformed OSD option");
*n->val++ = 0;
- clist_add_tail(&osd_opts, &n->n);
+ clist_add_tail(opts, &n->n);
}
static char *
if (o->o.osd > 0)
osd_care = 1;
}
+ if (osd_repeat_interval)
+ osd_care = 1;
if (!leds_care && !osd_care)
{
seen_msg = 1;
if (!seen_msg)
{
- add_osd_opt("=%40f");
- add_osd_opt("=%40s");
- add_osd_opt("=");
- add_osd_opt("=(and %m more)");
+ add_osd_opt("=%40f", &osd_opts);
+ add_osd_opt("=%40s", &osd_opts);
+ add_osd_opt("=", &osd_opts);
+ add_osd_opt("=(and %N more)", &osd_opts);
}
}
sync_leds();
}
+static bool // 1 if string is to long
+append_osd_string(char * dest, unsigned *pos, char * source)
+{
+ *pos += snprintf(dest+*pos, OSD_MSG_SIZE-*pos-1, "%s", source);
+ if (*pos > OSD_MSG_SIZE-1)
+ {
+ *pos = sprintf(dest, "OSD message too long!");
+ return 1;
+ }
+ return 0;
+}
+
struct osd_params {
struct mbox *mbox;
- int total_new;
+ int new;
+ int flagged;
+ int total;
+ clist *mboxes;
};
+static int format_osd_string(char *dest, char *src, struct osd_params *par);
+
+static void
+format_osd_string_mailboxes(char * dest, bool all, struct osd_params *par)
+{
+ if (!par->mboxes)
+ {
+ sprintf(dest, "[no mboxes]");
+ return;
+ }
+ unsigned pos = 0;
+ struct osd_params p = *par;
+ bool is_first=1;
+ CLIST_FOR_EACH(struct mbox *, b, *par->mboxes)
+ {
+ if (b->o.osd_list)
+ if (all || b->new)
+ {
+ p.mboxes = 0;
+ p.mbox = b;
+ p.new = b->new;
+ p.flagged = b->flagged;
+ p.total = b->total;
+ char buf[OSD_MSG_SIZE];
+ if (!is_first)
+ {
+ if (format_osd_string(buf, osd_mbox_opt_separator, &p))
+ if (append_osd_string(dest, &pos, buf)) break;
+ }
+ if (!format_osd_string(buf, osd_mbox_opt, &p))
+ continue;
+ if (append_osd_string(dest, &pos, buf)) break;
+ is_first = 0;
+ }
+ }
+ dest[pos] = 0;
+}
+
static int
format_osd_string(char *dest, char *src, struct osd_params *par)
{
char *stop = dest + OSD_MSG_SIZE - 1;
- char numbuf[16];
+ char buf[OSD_MSG_SIZE];
while (*src && dest < stop)
{
if (!spec)
break;
- char *arg = numbuf;
+ char *arg = buf;
switch (spec)
{
case 'f':
- arg = par->mbox->sender_snippet;
+ arg = par->mbox ? par->mbox->sender_snippet : "";
break;
case 's':
- arg = par->mbox->subject_snippet;
+ arg = par->mbox ? par->mbox->subject_snippet : "";
+ break;
+ case 'a':
+ arg = par->mbox ? par->mbox->name : "";
+ break;
+ case 'A':
+ arg = par->mbox ? par->mbox->path : "";
break;
case 'n':
- snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
+ snprintf(buf, sizeof(buf), "%d", par->new);
break;
- case 'm':
- if (par->total_new < 2)
+ case 'F':
+ snprintf(buf, sizeof(buf), "%d", par->flagged);
+ break;
+ case 'N':
+ if (par->new < 2)
return 0;
- snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
+ snprintf(buf, sizeof(buf), "%d", par->new - 1);
+ break;
+ case 'm':
+ snprintf(buf, sizeof(buf), "%d", par->total);
+ break;
+ case 't':
+ snprintf(buf, sizeof(buf), "%lld", (long long)time(NULL));
+ break;
+ case 'b':
+ format_osd_string_mailboxes(buf, 0, par);
+ break;
+ case 'B':
+ format_osd_string_mailboxes(buf, 1, par);
break;
case '%':
arg = "%";
}
static void
-format_osd(char *msg, struct osd_params *par)
+format_osd(char *msg, struct clist *opts, struct osd_params *par)
{
- if (!par->mbox)
- {
- msg[0] = 0;
- return;
- }
-
unsigned pos = 0;
unsigned have_text = 0;
- CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+ CLIST_FOR_EACH(struct osd_opt_node *, n, *opts)
{
char buf[OSD_MSG_SIZE];
if (!format_osd_string(buf, n->val, par))
fprintf(stderr, ">\n");
}
+static void
+osd_send(char * msg)
+{
+ if (msg[0])
+ {
+ debug("OSD: Sending to daemon\n");
+ XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) msg, strlen(msg));
+ XFlush(x11_dpy);
+ }
+}
+
+static struct osd_params
+create_osd_params(bool (* if_use_box)(struct mbox *))
+{
+ struct osd_params p = { .mbox = NULL, .new = 0, .flagged=0, .total=0, .mboxes=&mboxes };
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ if (if_use_box(b))
+ {
+ p.new += b->new;
+ p.flagged += b->flagged;
+ p.total += b->new;
+ if (b->new && b->best_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
+ p.mbox = b;
+ }
+ return p;
+}
+
+static bool if_use_box_osd(struct mbox *b)
+{
+ return b->o.osd > 0;
+}
+
+static bool if_use_box_osd_repeat(struct mbox *b)
+{
+ return b->o.osd_repeat > 0;
+}
+
static void
rethink_osd(int notify)
{
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->best_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
- p.mbox = b;
- }
+ struct osd_params p = create_osd_params(if_use_box_osd);
char new_msg[OSD_MSG_SIZE];
- format_osd(new_msg, &p);
+ if(p.mbox)
+ format_osd(new_msg, &osd_opts, &p);
+ else
+ new_msg[0]='\0';
debug_osd_msg(new_msg);
if (strcmp(new_msg, osd_last_msg))
{
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);
- }
+ if (notify)
+ osd_send(new_msg);
else
debug("OSD: No changes\n");
osd_last_time = time(NULL);
sync_leds();
}
+static void
+osd_repeat(void)
+{
+ if(!osd_repeat_interval) return;
+ last_osd_repeat_time = time(NULL);
+ char new_msg[OSD_MSG_SIZE];
+ struct osd_params p = create_osd_params(if_use_box_osd_repeat);
+ format_osd(new_msg, &osd_repeat_opts, &p);
+ debug_osd_msg(new_msg);
+ osd_send(new_msg);
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ {
+ b->osd_repeat_last_total = b->total ;
+ b->osd_repeat_last_new = b->new ;
+ b->osd_repeat_last_flagged = b->flagged ;
+ strcpy(b->osd_repeat_last_sender_snippet , b->sender_snippet );
+ strcpy(b->osd_repeat_last_subject_snippet , b->subject_snippet);
+ }
+}
+
+static void
+rethink_osd_repeat(void)
+{
+ if(!osd_repeat_interval) return;
+ bool change = 0;
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ {
+ if( b->osd_repeat_last_total != b->total ) change = 1;
+ if( b->osd_repeat_last_new != b->new ) change = 1;
+ if( b->osd_repeat_last_flagged != b->flagged ) change = 1;
+ if(strcmp(b->osd_repeat_last_sender_snippet , b->sender_snippet )) change = 1;
+ if(strcmp(b->osd_repeat_last_subject_snippet , b->subject_snippet)) change = 1;
+ }
+ if(change) osd_repeat();
+}
+
#else
static void x11_init(void) { }
static void rethink_leds(void) { }
static void rethink_osd(int notify UNUSED) { }
static void x11_cleanup(void) { }
+static void osd_repeat(void) { }
+static void rethink_osd_repeat(void) { }
#endif
}
rethink_leds();
rethink_osd(notify);
+ rethink_osd_repeat();
if (beeeep && allow_bells && notify)
beep();
}
-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\
+-r <key>=<val>\t\tSet on-screen display repeat notification options (consult OSDD docs)\n\
+-b <val>\t\tSet on-screen display mailbox specification string (for %%b %%B expansions)\n\
+-B <val>\t\tSet on-screen display mailbox specification string separator (for %%b %%B expansions)\n\
+-C <interval>\t\tSend repeat notifications every <interval> seconds (default is disable repeat not.)\n\
-t\t\t\tLet TAB select the next mailbox with new mail, no matter what priority it has\n\
-f\t\t\tEnable inotify (wait for file system changes)\n\
\n\
-On-screen display replacement (in all OSD values and notification lines):\n\
+On-screen display replacement (in all OSD values, notification lines and -b -B options):\n\
%%%%\t\t%%\n\
-%%f\t\tFrom - mail author name\n\
-%%s\t\tMail subject\n\
+%%f\t\tFrom - mail author name (last unread mail if any exist)\n\
+%%s\t\tMail subject (last unread mail if any exist)\n\
+%%a\t\tMailbox name of last unread mail (if any exit) or actual mailbox in -b\n\
+%%A\t\tMailbox path of last unread mail (if any exit) or actual mailbox in -b\n\
%%n\t\tCount of new mails\n\
-%%m\t\tCount of new mails minus one; if is less than one, whole line will be hidden\n\
+%%N\t\tCount of new mails minus one; if is less than one, whole line will be hidden\n\
+%%F\t\tCount of flagged mails\n\
+%%m\t\tCount of all mails\n\
+%%t\t\tActual unix time\n\
+%%b\t\tPrint all mailboxes with at least one new mail (format by -b -B options)\n\
+%%B\t\tPrint all mailbox (format by -b -B options)\n\
\n\
Mailbox options (set with `-o', use upper case to negate):\n\
0-9\t\t\tSet mailbox priority (0=default)\n\
b\t\t\tBeep when a message arrives\n\
d\t\t\tSend an on-screen-display message (requires OSDD)\n\
+x\t\t\tCount this mailbox in on-screen display repeat notification (default on)\n\
+y\t\t\tUse this mailbox in notification mailbox list (%%b %%B expansions) (default on)\n\
e\t\t\tHide from display if empty\n\
f\t\t\tShow flagged messages if there are no new ones\n\
h\t\t\tHide from display\n\
case 'b':
o->beep = value;
break;
+ case 'x':
+ o->osd_repeat = value;
+ break;
+ case 'y':
+ o->osd_list = value;
+ break;
case 'd':
o->osd = value;
break;
clist_init(&options);
clist_init(&patterns);
clist_init(&osd_opts);
+ clist_init(&osd_repeat_opts);
int c;
- while ((c = getopt(argc, argv, "c:dim:o:p:s:tif")) >= 0)
+ while ((c = getopt(argc, argv, "c:C:dim:o:p:s:r:b:B:tif")) >= 0)
switch (c)
{
case 'c':
if (check_interval <= 0)
usage();
break;
+ case 'C':
+ osd_repeat_interval = atol(optarg);
+ if (check_interval <= 0)
+ usage();
+ break;
case 'd':
debug_mode++;
break;
minimum_priority = atol(optarg);
break;
case 's':
- add_osd_opt(optarg);
+ add_osd_opt(optarg, &osd_opts);
+ break;
+ case 'r':
+ add_osd_opt(optarg, &osd_repeat_opts);
+ break;
+ case 'b':
+ osd_mbox_opt = optarg;
+ break;
+ case 'B':
+ osd_mbox_opt_separator = optarg;
break;
case 't':
simple_tab = 1;
{
time_t now = time(NULL);
int remains = last_scan_time + check_interval - now;
+ int osd_repeat_remains = last_osd_repeat_time + osd_repeat_interval - now;
+ if(!osd_repeat_interval) osd_repeat_remains = 1<<30;
if (force_refresh)
scan_and_redraw(0);
if (remains <= 0 || inotify_rescan)
inotify_rescan = 0;
scan_and_redraw(1);
}
+ if (osd_repeat_remains <= 0)
+ {
+ osd_repeat();
+ }
else
{
nodelay(stdscr,1);
int ch = getch();
if (ch < 0)
{
- inotify_rescan |= inotify_wait(remains);
+ int min_remains = remains;
+ if(min_remains > osd_repeat_remains) min_remains = osd_repeat_remains;
+ inotify_rescan |= inotify_wait(min_remains);
ch = getch();
}
if (ch < 0)