]> mj.ucw.cz Git - checkmail.git/blobdiff - cm.c
Add OSD repeat notification.
[checkmail.git] / 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;
 #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 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;
 
 static char *run_cmd = "mutt -f %s";
 static int simple_tab;
 
@@ -56,6 +58,8 @@ struct options {
   int hotkey;
   int led;
   int osd;
   int hotkey;
   int led;
   int osd;
+  int osd_repeat;
+  int osd_list;
   int unread_is_new;
   int sort_order;
 };
   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,
 static struct options global_options = {
   .sender_personal = 1,
   .sort_order = 1000,
+  .osd_repeat = 1,
+  .osd_list = 1,
 };
 
 #define MDIR_MAX_NAME_LEN 128
 };
 
 #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];
   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 mboxes;
@@ -121,6 +130,9 @@ struct osd_opt_node {
 };
 
 static clist osd_opts;
 };
 
 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);
 
 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->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;
 }
   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(hotkey);
        MERGE(led);
        MERGE(osd);
+       MERGE(osd_repeat);
+       MERGE(osd_list);
        MERGE(unread_is_new);
        MERGE(sort_order);
       }
 }
 
 static void
        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);
 {
   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;
   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 *
 }
 
 static char *
@@ -918,6 +934,8 @@ x11_init(void)
       if (o->o.osd > 0)
        osd_care = 1;
     }
       if (o->o.osd > 0)
        osd_care = 1;
     }
+  if (osd_repeat_interval)
+    osd_care = 1;
 
   if (!leds_care && !osd_care)
     {
 
   if (!leds_care && !osd_care)
     {
@@ -945,10 +963,10 @@ x11_init(void)
          seen_msg = 1;
       if (!seen_msg)
        {
          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();
 }
 
   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;
 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;
 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)
     {
 
   while (*src && dest < stop)
     {
@@ -1014,22 +1085,43 @@ format_osd_string(char *dest, char *src, struct osd_params *par)
          if (!spec)
            break;
 
          if (!spec)
            break;
 
-         char *arg = numbuf;
+         char *arg = buf;
          switch (spec)
            {
            case 'f':
          switch (spec)
            {
            case 'f':
-             arg = par->mbox->sender_snippet;
+             arg = par->mbox ? par->mbox->sender_snippet : "";
              break;
            case 's':
              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':
              break;
            case 'n':
-             snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
+             snprintf(buf, sizeof(buf), "%d", par->new);
              break;
              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;
                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 = "%";
              break;
            case '%':
              arg = "%";
@@ -1053,17 +1145,11 @@ format_osd_string(char *dest, char *src, struct osd_params *par)
 }
 
 static void
 }
 
 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;
   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))
     {
       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");
 }
 
   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)
 {
 static void
 rethink_osd(int notify)
 {
@@ -1107,27 +1230,19 @@ rethink_osd(int notify)
       return;
     }
 
       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];
 
   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);
   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);
       else
        debug("OSD: No changes\n");
       osd_last_time = time(NULL);
@@ -1144,12 +1259,50 @@ x11_cleanup(void)
   sync_leds();
 }
 
   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) { }
 #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
 
 
 #endif
 
@@ -1338,6 +1491,7 @@ rethink_display(int notify)
     }
   rethink_leds();
   rethink_osd(notify);
     }
   rethink_leds();
   rethink_osd(notify);
+  rethink_osd_repeat();
   if (beeeep && allow_bells && notify)
     beep();
 }
   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\
 -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\
 -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\
 %%%%\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\
 %%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\
 \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\
 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 '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;
          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(&options);
   clist_init(&patterns);
   clist_init(&osd_opts);
+  clist_init(&osd_repeat_opts);
 
   int c;
 
   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':
     switch (c)
       {
       case 'c':
@@ -1840,6 +2014,11 @@ main(int argc, char **argv)
        if (check_interval <= 0)
          usage();
        break;
        if (check_interval <= 0)
          usage();
        break;
+      case 'C':
+       osd_repeat_interval = atol(optarg);
+       if (check_interval <= 0)
+         usage();
+       break;
       case 'd':
        debug_mode++;
        break;
       case 'd':
        debug_mode++;
        break;
@@ -1856,7 +2035,16 @@ main(int argc, char **argv)
        minimum_priority = atol(optarg);
        break;
       case 's':
        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;
        break;
       case 't':
        simple_tab = 1;
@@ -1885,6 +2073,8 @@ restart:
     {
       time_t now = time(NULL);
       int remains = last_scan_time + check_interval - now;
     {
       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)
       if (force_refresh)
        scan_and_redraw(0);
       if (remains <= 0 || inotify_rescan)
@@ -1893,13 +2083,19 @@ restart:
          inotify_rescan = 0;
          scan_and_redraw(1);
        }
          inotify_rescan = 0;
          scan_and_redraw(1);
        }
+      if (osd_repeat_remains <= 0)
+       {
+         osd_repeat();
+       }
       else
        {
          nodelay(stdscr,1);
          int ch = getch();
          if (ch < 0)
            {
       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)
              ch = getch();
            }
          if (ch < 0)