]> mj.ucw.cz Git - osdd.git/commitdiff
Initial commit.
authorMartin Mares <mj@ucw.cz>
Sat, 17 Jul 2010 14:37:06 +0000 (16:37 +0200)
committerMartin Mares <mj@ucw.cz>
Sat, 17 Jul 2010 14:37:06 +0000 (16:37 +0200)
Makefile [new file with mode: 0644]
osdc.c [new file with mode: 0644]
osdd.c [new file with mode: 0644]
send.c [new file with mode: 0644]
send.h [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d5f5351
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99
+
+all: osdd osdc
+
+osdd: osdd.o util.o
+osdc: osdc.o util.o send.o
+
+osdd.o: CFLAGS+=$(shell xosd-config --cflags)
+osdd: LDFLAGS+=$(shell xosd-config --libs)
+
+osdc: LDFLAGS+=-lX11
+
+clean:
+       rm -f *~ *.o TAGS core osdd
diff --git a/osdc.c b/osdc.c
new file mode 100644 (file)
index 0000000..27b6c63
--- /dev/null
+++ b/osdc.c
@@ -0,0 +1,54 @@
+/*
+ *     On-screen Display Client
+ *
+ *     (c) 2010 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <xosd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "util.h"
+#include "send.h"
+
+static void NONRET
+usage(void)
+{
+  fprintf(stderr, "Usage: osdc [--<property>=<value> | <message>]*\n");
+  exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  struct osd_msg *m = osd_new_msg();
+
+  int more_opts = 1;
+  for (int i=1; i<argc; i++)
+    {
+      char *arg = argv[i];
+      if (more_opts && arg[0] == '-' && arg[1] == '-')
+       {
+         if (!arg[2])
+           more_opts = 0;
+         else
+           {
+             arg += 2;
+             if (!strcmp(arg, "help"))
+               usage();
+             char *sep = strchr(arg, '=');
+             if (!sep)
+               usage();
+             *sep++ = 0;
+             osd_add_line(m, arg, sep);
+           }
+       }
+      else
+       osd_add_line(m, NULL, arg);
+    }
+
+  osd_send(m);
+  return 0;
+}
diff --git a/osdd.c b/osdd.c
new file mode 100644 (file)
index 0000000..800f6ab
--- /dev/null
+++ b/osdd.c
@@ -0,0 +1,217 @@
+/*
+ *     On-screen Display Daemon
+ *
+ *     (c) 2010 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <sys/time.h>
+#include <xosd.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#define DEBUG
+#include "util.h"
+
+static xosd *osd;
+#define MAX_LINES 4
+
+typedef uint64_t timestamp_t;
+static timestamp_t now;
+
+/*** Displaying of messages ***/
+
+struct msg {
+  struct msg *next;
+  timestamp_t min_light, max_light;
+  char text[1];
+};
+
+static void
+display_msg(struct msg *msg)
+{
+  msg->min_light = msg->max_light = now + 1000;
+
+  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.
+      char *nl = strchr(line, '\n');
+      *nl++ = 0;
+
+      char *key;
+      char *val = strchr(line, ':');
+      if (val)
+       {
+         key = line;
+         *val++ = 0;
+       }
+      else
+       {
+         key = "";
+         val = line;
+       }
+
+      if (!key[0])
+       {
+         if (row < MAX_LINES)
+           xosd_display(osd, row++, XOSD_string, val);
+       }
+      else if (!strcmp(key, "duration"))
+       msg->max_light = now + atoi(val);
+      else if (!strcmp(key, "min-duration"))
+       msg->min_light = now + atoi(val);
+
+      line = nl;
+    }
+
+  if (msg->min_light > msg->max_light)
+    msg->min_light = msg->max_light;
+}
+
+static void
+hide_msg(struct msg *msg)
+{
+  xosd_scroll(osd, MAX_LINES);
+  xosd_hide(osd);
+  free(msg);
+}
+
+/*** The message queue ***/
+
+static struct msg *current_msg, *first_msg, *last_msg;
+
+static void
+enqueue_msg(unsigned char *buf, int len)
+{
+  DBG("[%.*s]\n", len, buf);
+  if (!len || buf[len-1] != '\n')
+    return;
+
+  struct msg *msg = xmalloc(sizeof(*msg) + len);
+  memcpy(msg->text, buf, len);
+  msg->text[len] = 0;
+
+  if (first_msg)
+    last_msg->next = msg;
+  else
+    first_msg = msg;
+  last_msg = msg;
+  msg->next = NULL;
+}
+
+static void
+parse_input(unsigned char *buf, int len)
+{
+  /* The property might contain several messages concatenated. Split them. */
+  while (len > 0)
+    {
+      if (buf[0] == '\n')
+       {
+         buf++, len--;
+         continue;
+       }
+      int i = 0;
+      while (i < len && (buf[i] != '\n' || (i && buf[i-1] != '\n')))
+       i++;
+      enqueue_msg(buf, i);
+      buf += i, len -= i;
+    }
+}
+
+/*** Main loop ***/
+
+int
+main(void)
+{
+  XInitThreads();
+
+  Display *dpy = XOpenDisplay(NULL);
+  if (!dpy)
+    die("Cannot open display");
+  Window win = DefaultRootWindow(dpy);
+
+  Atom pty = XInternAtom(dpy, "OSD_QUEUE", False);
+  if (!pty)
+    die("Cannot intern OSD_QUEUE atom");
+
+  XSelectInput(dpy, win, PropertyChangeMask);
+  XDeleteProperty(dpy, win, pty);
+  XFlush(dpy);
+
+  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);
+
+  struct pollfd pfd = {
+    .fd = ConnectionNumber(dpy),
+    .events = POLLIN,
+  };
+
+  for (;;)
+    {
+      struct timeval tv;
+      gettimeofday(&tv, NULL);
+      now = (timestamp_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+      timestamp_t wait_until = now - 1;
+      if (!current_msg && first_msg)
+       {
+         current_msg = first_msg;
+         first_msg = first_msg->next;
+         display_msg(current_msg);
+       }
+      if (current_msg)
+       {
+         if (first_msg)
+           wait_until = current_msg->min_light;
+         else
+           wait_until = current_msg->max_light;
+         if (wait_until <= now)
+           {
+             hide_msg(current_msg);
+             current_msg = NULL;
+             continue;
+           }
+       }
+
+      DBG("Waiting for %d ms\n", (int)(wait_until - now));
+      poll(&pfd, 1, wait_until - now);
+      if (pfd.revents & POLLIN)
+       {
+         while (XPending(dpy))
+           {
+             XEvent ev;
+             XNextEvent(dpy, &ev);
+             if (ev.type != PropertyNotify)
+               continue;
+             XPropertyEvent *p = &ev.xproperty;
+             if (p->window == win && p->atom == pty)
+               {
+                 Atom pty_type;
+                 int pty_fmt;
+                 unsigned long pty_items, pty_remains;
+                 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);
+                   }
+                 if (pty_buf)
+                   XFree(pty_buf);
+               }
+           }
+       }
+    }
+}
diff --git a/send.c b/send.c
new file mode 100644 (file)
index 0000000..aab11a7
--- /dev/null
+++ b/send.c
@@ -0,0 +1,67 @@
+/*
+ *     On-screen Display Client -- Sending Messages
+ *
+ *     (c) 2010 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "util.h"
+#include "send.h"
+
+static Display *dpy;
+static Atom pty;
+
+#define MAX_MSG_SIZE 1024
+
+struct osd_msg {
+  int cnt;
+  char buf[MAX_MSG_SIZE];
+};
+
+void
+osd_init(void)
+{
+  dpy = XOpenDisplay(NULL);
+  if (!dpy)
+    die("Cannot open display");
+
+  pty = XInternAtom(dpy, "OSD_QUEUE", False);
+  if (!pty)
+    die("Cannot intern OSD_QUEUE atom");
+}
+
+struct osd_msg *
+osd_new_msg(void)
+{
+  struct osd_msg *msg = xmalloc(sizeof(*msg));
+  msg->cnt = 0;
+  return msg;
+}
+
+void
+osd_add_line(struct osd_msg *msg, char *key, char *val)
+{
+  if (!key)
+    key = "";
+  msg->cnt += snprintf(msg->buf + msg->cnt, MAX_MSG_SIZE - msg->cnt - 1, "%s:%s\n", key, val);
+  if (msg->cnt > MAX_MSG_SIZE - 1)
+    die("OSD message too long (at most %d bytes)", MAX_MSG_SIZE);
+}
+
+void
+osd_send(struct osd_msg *msg)
+{
+  if (!dpy)
+    osd_init();
+
+  msg->buf[msg->cnt++] = '\n';
+  if (!XChangeProperty(dpy, DefaultRootWindow(dpy), pty, XA_STRING, 8, PropModeAppend, (unsigned char *) msg->buf, msg->cnt))
+    die("XChangeProperty failed");
+  XFlush(dpy);
+  free(msg);
+}
diff --git a/send.h b/send.h
new file mode 100644 (file)
index 0000000..e01022d
--- /dev/null
+++ b/send.h
@@ -0,0 +1,12 @@
+/*
+ *     On-screen Display Client -- Sending Messages
+ *
+ *     (c) 2010 Martin Mares <mj@ucw.cz>
+ */
+
+void osd_init(void);
+
+struct osd_msg;
+struct osd_msg *osd_new_msg(void);
+void osd_add_line(struct osd_msg *msg, char *key, char *val);
+void osd_send(struct osd_msg *msg);
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..fdf8b76
--- /dev/null
+++ b/util.c
@@ -0,0 +1,31 @@
+/*
+ *     On-screen Display Daemon -- Utility Functions
+ *
+ *     (c) 2010 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "util.h"
+
+void __attribute__((noreturn)) __attribute__((format(printf,1,2)))
+die(char *fmt, ...)
+{
+  va_list args;
+  va_start(args, fmt);
+  fputs("osdd: ", stderr);
+  vfprintf(stderr, fmt, args);
+  fputc('\n', stderr);
+  exit(1);
+}
+
+void *
+xmalloc(int size)
+{
+  void *p = malloc(size);
+  if (!p)
+    die("Failed to allocate %d bytes of memory", size);
+  return p;
+}
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..23645aa
--- /dev/null
+++ b/util.h
@@ -0,0 +1,18 @@
+/*
+ *     On-screen Display Daemon -- Utility Functions
+ *
+ *     (c) 2010 Martin Mares <mj@ucw.cz>
+ */
+
+#define NONRET __attribute__((noreturn))
+#define FORMAT_CHECK(func,i,j) __attribute__((format(func,i,j)))
+
+void NONRET FORMAT_CHECK(printf,1,2) die(char *fmt, ...);
+
+#ifdef DEBUG
+#define DBG(f...) printf(f)
+#else
+#define DBG(f...) do { } while(0)
+#endif
+
+void *xmalloc(int size);