]> mj.ucw.cz Git - osdd.git/blobdiff - osdd.c
Makefile: Link libX11 explicitly
[osdd.git] / osdd.c
diff --git a/osdd.c b/osdd.c
index 800f6ab9467a61e81fde8f394a7cb8886e547233..a3364fc8526306a91996b5fdd65a551b3d78a9cf 100644 (file)
--- a/osdd.c
+++ b/osdd.c
 /*
  *     On-screen Display Daemon
  *
- *     (c) 2010 Martin Mares <mj@ucw.cz>
+ *     (c) 2010--2014 Martin Mares <mj@ucw.cz>
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <inttypes.h>
+#include <unistd.h>
 #include <poll.h>
-#include <sys/time.h>
-#include <xosd.h>
+#include <getopt.h>
+#include <locale.h>
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 
-#define DEBUG
+#undef DEBUG
 #include "util.h"
+#include "display.h"
 
-static xosd *osd;
-#define MAX_LINES 4
+static struct osd_state *osd;
 
-typedef uint64_t timestamp_t;
 static timestamp_t now;
 
+/*** Options ***/
+
+static char *font_name = "times-64:bold";
+static char *default_color = "green";
+static char *default_outline_color = "black";
+static int default_outline_width = 2;
+static int default_duration = 1000;
+static int default_min_duration = 250;
+static int debug_mode;
+static int test_mode;
+static double line_spacing = 0.2;
+
+static const char short_opts[] = "c:d:Df:m:o:O:s:";
+
+enum long_opt {
+  OPT_TEST = 256,
+};
+
+static const struct option long_opts[] = {
+  { "color",           required_argument,      NULL,   'c' },
+  { "debug",           no_argument,            NULL,   'D' },
+  { "duration",                required_argument,      NULL,   'd' },
+  { "font",            required_argument,      NULL,   'f' },
+  { "min-duration",    required_argument,      NULL,   'm' },
+  { "outline-color",   required_argument,      NULL,   'o' },
+  { "outline-width",   required_argument,      NULL,   'O' },
+  { "line-spacing",    required_argument,      NULL,   's' },
+  { "test",            no_argument,            NULL,   OPT_TEST },     // Undocumented test mode
+  { NULL,              0,                      NULL,   0   },
+};
+
+static void NONRET
+usage(void)
+{
+  fprintf(stderr, "Usage: osdd <options>\n\n\
+Options:\n\
+-c, --color=<c>\t\tDefault color (#rgb, #rrggbb or a name from rgb.txt)\n\
+-D, --debug\t\tDebugging mode (do not detach from the terminal)\n\
+-d, --duration=<ms>\tDefault message duration in milliseconds\n\
+-f, --font=<f>\t\tFont to use for the OSD\n\
+-m, --min-duration=<ms>\tDefault minimum message duration in milliseconds\n\
+-o, --outline-color=<c>\tDefault outline color\n\
+-O, --outline-width=<n>\tDefault outline width (default=2)\n\
+-s, --line-spacing=<n>\tSet line spacing factor (decimal fraction, default=0.2)\n\
+");
+  exit(1);
+}
+
+static void
+parse_opts(int argc, char **argv)
+{
+  int opt;
+  while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
+    switch (opt)
+      {
+      case 'c':
+       default_color = optarg;
+       break;
+      case 'd':
+       default_duration = atoi(optarg);
+       break;
+      case 'D':
+       debug_mode = 1;
+       break;
+      case 'f':
+       font_name = optarg;
+       break;
+      case 'l':
+       line_spacing = atof(optarg);
+       break;
+      case 'm':
+       default_min_duration = atoi(optarg);
+       break;
+      case 'o':
+       default_outline_color = optarg;
+       break;
+      case 'O':
+       default_outline_width = atoi(optarg);
+       break;
+      case OPT_TEST:
+       test_mode = 1;
+       debug_mode = 1;
+       break;
+      default:
+       usage();
+      }
+
+  if (optind < argc)
+    usage();
+}
+
 /*** Displaying of messages ***/
 
 struct msg {
@@ -34,13 +124,17 @@ struct msg {
 static void
 display_msg(struct msg *msg)
 {
-  msg->min_light = msg->max_light = now + 1000;
+  DBG("## Displaying message\n");
+  msg->min_light = now + default_min_duration;
+  msg->max_light = now + default_duration;
+  char *fg_color = default_color;
+  char *outline_color = default_outline_color;
+  int outline_width = default_outline_width;
 
   char *line = msg->text;
-  int row = 0;
   while (*line)
     {
-      // The parser it destructive, but it does not harm, since we display each message only once.
+      // The parser it destructive, but it does not do any harm, since we display each message only once.
       char *nl = strchr(line, '\n');
       *nl++ = 0;
 
@@ -56,29 +150,56 @@ display_msg(struct msg *msg)
          key = "";
          val = line;
        }
+      DBG("\t%s:%s\n", key, val);
 
+      struct osd_line *l = NULL;
       if (!key[0])
        {
-         if (row < MAX_LINES)
-           xosd_display(osd, row++, XOSD_string, val);
+         l = osd_add_line(osd, OSD_TYPE_TEXT);
+         sprintf(l->u.text, "%.*s", OSD_MAX_LINE_LEN, val);
+       }
+      else if (!strcmp(key, "percentage") || !strcmp(key, "percent"))
+       {
+         l = osd_add_line(osd, OSD_TYPE_PERCENTAGE);
+         l->u.percent = atoi(val);
+       }
+      else if (!strcmp(key, "slider"))
+       {
+         l = osd_add_line(osd, OSD_TYPE_SLIDER);
+         l->u.percent = atoi(val);
        }
       else if (!strcmp(key, "duration"))
        msg->max_light = now + atoi(val);
       else if (!strcmp(key, "min-duration"))
        msg->min_light = now + atoi(val);
+      else if (!strcmp(key, "color"))
+       fg_color = val;
+      else if (!strcmp(key, "outline-color"))
+       outline_color = val;
+      else if (!strcmp(key, "outline-width"))
+       outline_width = atoi(val);
+
+      if (l)
+       {
+         l->fg_color = fg_color;
+         l->outline_color = outline_color;
+         l->outline_width = outline_width;
+       }
 
       line = nl;
     }
 
   if (msg->min_light > msg->max_light)
     msg->min_light = msg->max_light;
+
+  osd_show(osd);
 }
 
 static void
 hide_msg(struct msg *msg)
 {
-  xosd_scroll(osd, MAX_LINES);
-  xosd_hide(osd);
+  DBG("## Hiding message\n");
+  osd_clear(osd);
   free(msg);
 }
 
@@ -89,7 +210,7 @@ static struct msg *current_msg, *first_msg, *last_msg;
 static void
 enqueue_msg(unsigned char *buf, int len)
 {
-  DBG("[%.*s]\n", len, buf);
+  DBG("Received: [%.*s]\n", len, buf);
   if (!len || buf[len-1] != '\n')
     return;
 
@@ -124,11 +245,26 @@ parse_input(unsigned char *buf, int len)
     }
 }
 
+static void
+do_test(void)
+{
+  unsigned char buf[4096];
+  int len = 0;
+  int c;
+
+  while ((c = read(0, buf + len, 4096 - len)) > 0)
+    len += c;
+  if (len)
+    enqueue_msg(buf, len);
+}
+
 /*** Main loop ***/
 
 int
-main(void)
+main(int argc, char **argv)
 {
+  parse_opts(argc, argv);
+  setlocale(LC_CTYPE, "");
   XInitThreads();
 
   Display *dpy = XOpenDisplay(NULL);
@@ -140,18 +276,30 @@ main(void)
   if (!pty)
     die("Cannot intern OSD_QUEUE atom");
 
-  XSelectInput(dpy, win, PropertyChangeMask);
-  XDeleteProperty(dpy, win, pty);
-  XFlush(dpy);
+  if (!debug_mode)
+    {
+      pid_t pid = fork();
+      if (pid < 0)
+       die("Cannot fork: %m");
+      if (pid > 0)
+        return 0;
+      setsid();
+    }
 
-  osd = xosd_create(4);
-  if (!osd)
-    die("Cannot initialize OSD");
-  xosd_set_font(osd, "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*");
-  xosd_set_outline_offset(osd, 2);
-  xosd_set_outline_colour(osd, "black");
-  xosd_set_pos(osd, XOSD_middle);
-  xosd_set_align(osd, XOSD_center);
+  if (test_mode)
+    {
+      do_test();
+      pty = 0;
+    }
+  else
+    {
+      XSelectInput(dpy, win, PropertyChangeMask);
+      XDeleteProperty(dpy, win, pty);
+      XFlush(dpy);
+    }
+
+  osd = osd_new(dpy);
+  osd_set_font(osd, font_name, line_spacing);
 
   struct pollfd pfd = {
     .fd = ConnectionNumber(dpy),
@@ -160,9 +308,7 @@ main(void)
 
   for (;;)
     {
-      struct timeval tv;
-      gettimeofday(&tv, NULL);
-      now = (timestamp_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
+      now = get_current_time();
 
       timestamp_t wait_until = now - 1;
       if (!current_msg && first_msg)
@@ -184,8 +330,10 @@ main(void)
              continue;
            }
        }
+      if (test_mode && !current_msg)
+       break;
 
-      DBG("Waiting for %d ms\n", (int)(wait_until - now));
+      DBG("... waiting for %d ms\n", (int)(wait_until - now));
       poll(&pfd, 1, wait_until - now);
       if (pfd.revents & POLLIN)
        {
@@ -193,6 +341,8 @@ main(void)
            {
              XEvent ev;
              XNextEvent(dpy, &ev);
+             if (osd_handle_event(osd, &ev))
+               continue;
              if (ev.type != PropertyNotify)
                continue;
              XPropertyEvent *p = &ev.xproperty;
@@ -204,10 +354,7 @@ main(void)
                  unsigned char *pty_buf = NULL;
                  XGetWindowProperty(dpy, win, pty, 0, 4096, True, XA_STRING, &pty_type, &pty_fmt, &pty_items, &pty_remains, &pty_buf);
                  if (pty_type == XA_STRING && pty_fmt == 8 && pty_items)
-                   {
-                     DBG("Received: <%.*s>\n", (int) pty_items, pty_buf);
-                     parse_input(pty_buf, pty_items);
-                   }
+                   parse_input(pty_buf, pty_items);
                  if (pty_buf)
                    XFree(pty_buf);
                }