]> mj.ucw.cz Git - checkmail.git/commitdiff
Add OSD repeat notification.
authorJiri Kalvoda <jirikalvoda@kam.mff.cuni.cz>
Sun, 18 Jul 2021 13:21:04 +0000 (15:21 +0200)
committerMartin Mares <mj@ucw.cz>
Fri, 21 Jan 2022 20:56:10 +0000 (21:56 +0100)
cm.c

diff --git a/cm.c b/cm.c
index 155a9208dce74f0d6b1ff4773ad628e4887706d1..01d683d9ec37dc4f979bc1187705b21d76a7691a 100644 (file)
--- a/cm.c
+++ b/cm.c
 #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 <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\
@@ -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)