]> mj.ucw.cz Git - checkmail.git/blobdiff - cm.c
OSD message options (and even messages themselves) made configurable
[checkmail.git] / cm.c
diff --git a/cm.c b/cm.c
index 36a29e9145e86904a10acb9bac8d0788cad1f56c..b9b2050266fced91e61fc8854426ee3cedd87fbc 100644 (file)
--- a/cm.c
+++ b/cm.c
@@ -1,9 +1,11 @@
 /*
  *     Incoming Mail Checker
  *
 /*
  *     Incoming Mail Checker
  *
- *     (c) 2005--2007 Martin Mares <mj@ucw.cz>
+ *     (c) 2005--2010 Martin Mares <mj@ucw.cz>
  */
 
  */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <time.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef CONFIG_WIDE_CURSES
+#include <ncursesw/ncurses.h>
+#else
 #include <curses.h>
 #include <curses.h>
+#endif
 
 #include "util.h"
 #include "clists.h"
 
 #include "util.h"
 #include "clists.h"
@@ -25,6 +34,7 @@
 static int check_interval = 30;
 static int force_refresh;
 static int allow_bells = 1;
 static int check_interval = 30;
 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 char *run_cmd = "mutt -f %s";
 static int minimum_priority;
 static time_t last_scan_time;
 static char *run_cmd = "mutt -f %s";
@@ -39,6 +49,9 @@ struct options {
   int show_flagged;
   int sender_personal;
   int sender_mbox;
   int show_flagged;
   int sender_personal;
   int sender_mbox;
+  int hotkey;
+  int led;
+  int osd;
 };
 
 struct option_node {
 };
 
 struct option_node {
@@ -80,6 +93,14 @@ static clist mboxes;
 static struct mbox **mbox_array;
 static int num_mboxes, mbox_array_size;
 
 static struct mbox **mbox_array;
 static int num_mboxes, mbox_array_size;
 
+struct osd_opt_node {
+  cnode n;
+  char *val;
+  char key[1];
+};
+
+static clist osd_opts;
+
 static void redraw_line(int i);
 static void rethink_display(void);
 
 static void redraw_line(int i);
 static void rethink_display(void);
 
@@ -121,6 +142,9 @@ init_options(struct options *o)
   o->show_flagged = -1;
   o->sender_personal = -1;
   o->sender_mbox = -1;
   o->show_flagged = -1;
   o->sender_personal = -1;
   o->sender_mbox = -1;
+  o->hotkey = -1;
+  o->led = -1;
+  o->osd = -1;
 }
 
 static void
 }
 
 static void
@@ -141,9 +165,24 @@ setup_options(struct mbox *b)
        MERGE(show_flagged);
        MERGE(sender_personal);
        MERGE(sender_mbox);
        MERGE(show_flagged);
        MERGE(sender_personal);
        MERGE(sender_mbox);
+       MERGE(hotkey);
+       MERGE(led);
+       MERGE(osd);
       }
 }
 
       }
 }
 
+static void
+add_osd_opt(char *arg)
+{
+  struct osd_opt_node *n = xmalloc(sizeof(*n) + strlen(arg));
+  strcpy(n->key, arg);
+  n->val = strchr(n->key, '=');
+  if (!n->val)
+    die("Malformed OSD option");
+  *n->val++ = 0;
+  clist_add_tail(&osd_opts, &n->n);
+}
+
 static char *
 mbox_name(char *path)
 {
 static char *
 mbox_name(char *path)
 {
@@ -303,6 +342,7 @@ scan_mbox(struct mbox *b, struct stat *st)
 {
   char buf[1024], sender[1024], subject[1024];
   int c;
 {
   char buf[1024], sender[1024], subject[1024];
   int c;
+  int compressed = 0;
   const char from[] = "\nFrom ";
 
   if (!st->st_size)
   const char from[] = "\nFrom ";
 
   if (!st->st_size)
@@ -312,7 +352,7 @@ scan_mbox(struct mbox *b, struct stat *st)
       return;
     }
 
       return;
     }
 
-  /* FIXME: Locking! */
+  /* FIXME: Should we do some locking? */
 
   mb_fd = open(b->path, O_RDONLY);
   if (mb_fd < 0)
 
   mb_fd = open(b->path, O_RDONLY);
   if (mb_fd < 0)
@@ -321,10 +361,39 @@ scan_mbox(struct mbox *b, struct stat *st)
       b->total = b->new = b->flagged = -1;
       return;
     }
       b->total = b->new = b->flagged = -1;
       return;
     }
+
+  char signature[2];
+  c = read(mb_fd, signature, 2);
+  lseek(mb_fd, 0, SEEK_SET);
+
+  if (c == 2 && !memcmp(signature, "\037\213", 2)) //gzip
+    {
+      debug("[decompressing] ");
+      int fds[2];
+      if (pipe(fds))
+       die("pipe failed: %m");
+      int pid = fork();
+      if (pid < 0)
+       die("fork failed: %m");
+      if (!pid)
+       {
+         if (dup2(mb_fd, 0) < 0 || dup2(fds[1], 1) < 0)
+           die("dup2 failed: %m");
+         close(fds[0]);
+         close(fds[1]);
+         close(mb_fd);
+         execlp("gzip", "gzip", "-cd", NULL);
+         die("Cannot execute gzip: %m");
+       }
+      close(fds[1]);
+      close(mb_fd);
+      mb_fd = fds[0];
+      compressed = 1;
+    }
   mb_reset(0);
 
   int incremental = 0;
   mb_reset(0);
 
   int incremental = 0;
-  if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh)
+  if (b->last_size && b->last_pos && st->st_size > b->last_size && !b->force_refresh && !compressed)
     {
       mb_seek(b->last_pos);
       if (mb_check(from, 6))
     {
       mb_seek(b->last_pos);
       if (mb_check(from, 6))
@@ -439,6 +508,12 @@ scan_mbox(struct mbox *b, struct stat *st)
 
  done:
   close(mb_fd);
 
  done:
   close(mb_fd);
+  if (compressed)
+    {
+      int status;
+      if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status))
+       b->total = b->new = b->flagged = -1;
+    }
 }
 
 static void
 }
 
 static void
@@ -526,6 +601,146 @@ scan(void)
   rethink_display();
 }
 
   rethink_display();
 }
 
+#ifdef CONFIG_X11
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+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 void
+x11_init(void)
+{
+  leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
+  osd_care = (global_options.osd >= 0);
+  CLIST_FOR_EACH(struct option_node *, o, options)
+    {
+      if (o->o.led > 0)
+       leds_care |= (1 << o->o.led);
+      if (o->o.osd > 0)
+       osd_care = 1;
+    }
+
+  if (!leds_care && !osd_care)
+    {
+      debug("X11: No mailbox wants LEDs or OSD\n");
+      return;
+    }
+  if (!getenv("DISPLAY"))
+    {
+      debug("X11: Do not have X display\n");
+      return;
+    }
+  if (!(x11_dpy = XOpenDisplay(NULL)))
+    die("Cannot open X display, although the DISPLAY variable is set");
+
+  if (osd_care)
+    {
+      osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
+      if (!osd_pty)
+       die("Cannot intern OSD_QUEUE atom");
+
+      // If OSD options contain no message, add one
+      int seen_msg = 0;
+      CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+       if (!n->key[0])
+         seen_msg = 1;
+      if (!seen_msg)
+       add_osd_opt("=You have new mail");
+    }
+
+  leds_have = ~0U;
+  debug("X11: Initialized\n");
+}
+
+static void
+sync_leds(void)
+{
+  if (leds_want == leds_have)
+    return;
+
+  debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
+  for (int i=1; i<10; i++)
+    if (leds_care & (leds_have ^ leds_want) & (1 << i))
+      {
+       XKeyboardControl cc;
+       cc.led = i;
+       cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
+       XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
+      }
+  XFlush(x11_dpy);
+  leds_have = leds_want;
+}
+
+static void
+sync_osd(void)
+{
+  if (!osd_want || !allow_osd)
+    {
+      osd_have = 0;
+      return;
+    }
+  if (osd_have)
+    return;
+  debug("OSD: Displaying\n");
+
+  char msg[1024];
+  unsigned pos = 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)
+       {
+         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);
+}
+
+static void
+rethink_leds(void)
+{
+  if (!x11_dpy)
+    return;
+
+  leds_want = 0;
+  osd_want = 0;
+  CLIST_FOR_EACH(struct mbox *, b, mboxes)
+    {
+      if (b->o.led > 0 && b->new)
+       leds_want |= (1 << b->o.led);
+      if (b->o.osd > 0 && b->new)
+       osd_want = 1;
+    }
+  sync_leds();
+  sync_osd();
+}
+
+static void
+x11_cleanup(void)
+{
+  if (!x11_dpy)
+    return;
+
+  leds_want = 0;
+  sync_leds();
+}
+
+#else
+
+static void x11_init(void) { }
+static void rethink_leds(void) { }
+static void x11_cleanup(void) { }
+
+#endif
+
 static int cursor_at, cursor_max;
 
 enum {
 static int cursor_at, cursor_max;
 
 enum {
@@ -549,13 +764,15 @@ redraw_line(int i)
       int hi = b->o.highlight;
 
       attrset(attrs[cc][hi][M_IDLE]);
       int hi = b->o.highlight;
 
       attrset(attrs[cc][hi][M_IDLE]);
-      if (cc)
+      if (b->o.hotkey)
+       printw("%c ", b->o.hotkey);
+      else if (cc)
        printw("> ");
       else
        printw("  ");
       if (b->new)
        attrset(attrs[cc][hi][M_NEW]);
        printw("> ");
       else
        printw("  ");
       if (b->new)
        attrset(attrs[cc][hi][M_NEW]);
-      else if (b->flagged)
+      else if (b->flagged && b->o.show_flagged)
        attrset(attrs[cc][hi][M_FLAG]);
       printw("%-20s ", b->name);
       if (b->scanning < 0)
        attrset(attrs[cc][hi][M_FLAG]);
       printw("%-20s ", b->name);
       if (b->scanning < 0)
@@ -591,13 +808,14 @@ redraw_line(int i)
                printw("        ");
              snip = 1;
            }
                printw("        ");
              snip = 1;
            }
-         else if (b->flagged)
+         else if (b->flagged && b->o.show_flagged)
            {
              attrset(attrs[cc][hi][M_FLAG]);
              printw("%6d  ", b->flagged);
              attrset(attrs[cc][hi][M_IDLE]);
              printw("        ");
            {
              attrset(attrs[cc][hi][M_FLAG]);
              printw("%6d  ", b->flagged);
              attrset(attrs[cc][hi][M_IDLE]);
              printw("        ");
-             snip = b->o.show_flagged;
+             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 && b->snippet[0])
            {
@@ -605,7 +823,16 @@ redraw_line(int i)
              getyx(stdscr, yy, xx);
              int remains = COLS-1-xx;
              if (remains > 2)
              getyx(stdscr, yy, xx);
              int remains = COLS-1-xx;
              if (remains > 2)
-               printw("%-.*s", remains, b->snippet);
+               {
+#ifdef CONFIG_WIDE_CURSES
+                 size_t len = strlen(b->snippet)+1;
+                 wchar_t snip[len];
+                 mbstowcs(snip, b->snippet, len);
+                 addnwstr(snip, remains);
+#else
+                 printw("%-.*s", remains, b->snippet);
+#endif
+               }
            }
        }
     }
            }
        }
     }
@@ -670,6 +897,7 @@ rethink_display(void)
       redraw_all();
       refresh();
     }
       redraw_all();
       refresh();
     }
+  rethink_leds();
   if (beeeep && allow_bells)
     beep();
 }
   if (beeeep && allow_bells)
     beep();
 }
@@ -780,6 +1008,20 @@ next_active(int since, int step)
     move_cursor(besti);
 }
 
     move_cursor(besti);
 }
 
+static void
+mbox_run(struct mbox *b)
+{
+  char cmd[strlen(run_cmd) + strlen(b->path) + 16];
+  sprintf(cmd, run_cmd, b->path);
+  term_cleanup();
+  system(cmd);
+  term_init();
+  redraw_all();
+  refresh();
+  b->force_refresh = 1;
+  scan_and_redraw();
+}
+
 #define STR2(c) #c
 #define STR(c) STR2(c)
 
 #define STR2(c) #c
 #define STR(c) STR2(c)
 
@@ -796,17 +1038,21 @@ Options:\n\
 -o <pattern>=<opts>\tSet mailbox options\n\
 -o <opts>\t\tSet default options for all mailboxes\n\
 -p <pri>\t\tSet minimum priority to show\n\
 -o <pattern>=<opts>\tSet mailbox 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\
 \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\
 \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\
 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\
+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\
 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\
 m\t\t\tShow mailbox name of the sender\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\
+!<key>\t\t\tSet hot key\n\
 \n\
 CheckMail " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
 It can be freely distributed and used according to the GNU GPL v2.\n\
 \n\
 CheckMail " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
 It can be freely distributed and used according to the GNU GPL v2.\n\
@@ -836,6 +1082,10 @@ parse_options(char *c)
   while (x = *c++)
     if (x >= '0' && x <= '9')
       o->priority = x - '0';
   while (x = *c++)
     if (x >= '0' && x <= '9')
       o->priority = x - '0';
+    else if (x == '!' && *c)
+      o->hotkey = *c++;
+    else if (x == 'l' && *c >= '1' && *c <= '9')
+      o->led = *c++ - '0';
     else
       {
        int value = !!islower(x);
     else
       {
        int value = !!islower(x);
@@ -844,6 +1094,9 @@ parse_options(char *c)
          case 'b':
            o->beep = value;
            break;
          case 'b':
            o->beep = value;
            break;
+         case 'd':
+           o->osd = value;
+           break;
          case 'e':
            o->hide_if_empty = value;
            break;
          case 'e':
            o->hide_if_empty = value;
            break;
@@ -878,9 +1131,10 @@ main(int argc, char **argv)
   clist_init(&mboxes);
   clist_init(&options);
   clist_init(&patterns);
   clist_init(&mboxes);
   clist_init(&options);
   clist_init(&patterns);
+  clist_init(&osd_opts);
 
   int c;
 
   int c;
-  while ((c = getopt(argc, argv, "c:dim:o:p:")) >= 0)
+  while ((c = getopt(argc, argv, "c:dim:o:p:s:")) >= 0)
     switch (c)
       {
       case 'c':
     switch (c)
       {
       case 'c':
@@ -903,6 +1157,9 @@ main(int argc, char **argv)
       case 'p':
        minimum_priority = atol(optarg);
        break;
       case 'p':
        minimum_priority = atol(optarg);
        break;
+      case 's':
+       add_osd_opt(optarg);
+       break;
       default:
        usage();
       }
       default:
        usage();
       }
@@ -911,10 +1168,12 @@ main(int argc, char **argv)
 
   charset_init();
   term_init();
 
   charset_init();
   term_init();
+  x11_init();
   scan_and_redraw();
   next_active(0, 1);
 
   int should_exit = 0;
   scan_and_redraw();
   next_active(0, 1);
 
   int should_exit = 0;
+restart:
   while (!should_exit)
     {
       time_t now = time(NULL);
   while (!should_exit)
     {
       time_t now = time(NULL);
@@ -926,6 +1185,14 @@ main(int argc, char **argv)
          remains *= 10;
          halfdelay((remains > 255) ? 255 : remains);
          int ch = getch();
          remains *= 10;
          halfdelay((remains > 255) ? 255 : remains);
          int ch = getch();
+         for (int i=0; i<num_mboxes; i++)
+           if (ch == mbox_array[i]->o.hotkey)
+             {
+               if (i < cursor_max)
+                 cursor_at = i;
+               mbox_run(mbox_array[i]);
+               goto restart;
+             }
          switch (ch)
            {
            case 'q':
          switch (ch)
            {
            case 'q':
@@ -958,18 +1225,7 @@ main(int argc, char **argv)
            case '\r':
            case '\n':
              if (cursor_at < cursor_max)
            case '\r':
            case '\n':
              if (cursor_at < cursor_max)
-               {
-                 struct mbox *b = mbox_array[cursor_at];
-                 char cmd[strlen(run_cmd) + strlen(b->path) + 16];
-                 sprintf(cmd, run_cmd, b->path);
-                 term_cleanup();
-                 system(cmd);
-                 term_init();
-                 redraw_all();
-                 refresh();
-                 b->force_refresh = 1;
-                 scan_and_redraw();
-               }
+               mbox_run(mbox_array[cursor_at]);
              break;
            case 'l' & 0x1f:
              clearok(stdscr, TRUE);
              break;
            case 'l' & 0x1f:
              clearok(stdscr, TRUE);
@@ -987,6 +1243,14 @@ main(int argc, char **argv)
              allow_bells = 0;
              print_status("Bells and whistles are now disabled. Pssst!");
              break;
              allow_bells = 0;
              print_status("Bells and whistles are now disabled. Pssst!");
              break;
+           case 'd':
+             allow_osd = 1;
+             print_status("On-screen display is now enabled.");
+             break;
+           case 'D':
+             allow_osd = 0;
+             print_status("On-screen display is now disabled. Watch your step.");
+             break;
            default:
              if (ch >= '0' && ch <= '9')
                {
            default:
              if (ch >= '0' && ch <= '9')
                {
@@ -1000,6 +1264,7 @@ main(int argc, char **argv)
        }
     }
 
        }
     }
 
+  x11_cleanup();
   term_cleanup();
   return 0;
 }
   term_cleanup();
   return 0;
 }