From 8e7cc8635454d588d879dad00d0cd7bbb68f0594 Mon Sep 17 00:00:00 2001 From: Jiri Kalvoda Date: Sun, 18 Jul 2021 15:21:04 +0200 Subject: [PATCH] Add OSD repeat notification. --- cm.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 241 insertions(+), 45 deletions(-) diff --git a/cm.c b/cm.c index 155a920..01d683d 100644 --- a/cm.c +++ b/cm.c @@ -35,11 +35,13 @@ #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; @@ -56,6 +58,8 @@ struct options { int hotkey; int led; int osd; + int osd_repeat; + int osd_list; int unread_is_new; int sort_order; }; @@ -76,6 +80,8 @@ static clist options, patterns; static struct options global_options = { .sender_personal = 1, .sort_order = 1000, + .osd_repeat = 1, + .osd_list = 1, }; #define MDIR_MAX_NAME_LEN 128 @@ -108,6 +114,9 @@ struct mbox { 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; @@ -121,6 +130,9 @@ struct osd_opt_node { }; 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); @@ -172,6 +184,8 @@ init_options(struct options *o) 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; } @@ -197,13 +211,15 @@ setup_options(struct mbox *b) 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); @@ -211,7 +227,7 @@ add_osd_opt(char *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 * @@ -918,6 +934,8 @@ x11_init(void) if (o->o.osd > 0) osd_care = 1; } + if (osd_repeat_interval) + osd_care = 1; if (!leds_care && !osd_care) { @@ -945,10 +963,10 @@ x11_init(void) 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); } } @@ -988,16 +1006,69 @@ rethink_leds(void) 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) { @@ -1014,22 +1085,43 @@ format_osd_string(char *dest, char *src, struct osd_params *par) 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 = "%"; @@ -1053,17 +1145,11 @@ format_osd_string(char *dest, char *src, struct osd_params *par) } 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)) @@ -1098,6 +1184,43 @@ debug_osd_msg(char *msg) 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) { @@ -1107,27 +1230,19 @@ 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); @@ -1144,12 +1259,50 @@ x11_cleanup(void) 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 @@ -1338,6 +1491,7 @@ rethink_display(int notify) } rethink_leds(); rethink_osd(notify); + rethink_osd_repeat(); if (beeeep && allow_bells && notify) beep(); } @@ -1717,20 +1871,33 @@ Options:\n\ -o \t\tSet default options for all mailboxes\n\ -p \t\tSet minimum priority to show\n\ -s =\t\tSet on-screen display options (consult OSDD docs)\n\ +-r =\t\tSet on-screen display repeat notification options (consult OSDD docs)\n\ +-b \t\tSet on-screen display mailbox specification string (for %%b %%B expansions)\n\ +-B \t\tSet on-screen display mailbox specification string separator (for %%b %%B expansions)\n\ +-C \t\tSend repeat notifications every 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\ @@ -1789,6 +1956,12 @@ parse_options(char *c) 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; @@ -1830,9 +2003,10 @@ main(int argc, char **argv) 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': @@ -1840,6 +2014,11 @@ main(int argc, char **argv) if (check_interval <= 0) usage(); break; + case 'C': + osd_repeat_interval = atol(optarg); + if (check_interval <= 0) + usage(); + break; case 'd': debug_mode++; break; @@ -1856,7 +2035,16 @@ main(int argc, char **argv) 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; @@ -1885,6 +2073,8 @@ restart: { 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) @@ -1893,13 +2083,19 @@ restart: 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) -- 2.39.2