]> mj.ucw.cz Git - osdd.git/blob - osdd.c
Initial commit.
[osdd.git] / osdd.c
1 /*
2  *      On-screen Display Daemon
3  *
4  *      (c) 2010 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <inttypes.h>
11 #include <poll.h>
12 #include <sys/time.h>
13 #include <xosd.h>
14 #include <X11/Xlib.h>
15 #include <X11/Xatom.h>
16
17 #define DEBUG
18 #include "util.h"
19
20 static xosd *osd;
21 #define MAX_LINES 4
22
23 typedef uint64_t timestamp_t;
24 static timestamp_t now;
25
26 /*** Displaying of messages ***/
27
28 struct msg {
29   struct msg *next;
30   timestamp_t min_light, max_light;
31   char text[1];
32 };
33
34 static void
35 display_msg(struct msg *msg)
36 {
37   msg->min_light = msg->max_light = now + 1000;
38
39   char *line = msg->text;
40   int row = 0;
41   while (*line)
42     {
43       // The parser it destructive, but it does not harm, since we display each message only once.
44       char *nl = strchr(line, '\n');
45       *nl++ = 0;
46
47       char *key;
48       char *val = strchr(line, ':');
49       if (val)
50         {
51           key = line;
52           *val++ = 0;
53         }
54       else
55         {
56           key = "";
57           val = line;
58         }
59
60       if (!key[0])
61         {
62           if (row < MAX_LINES)
63             xosd_display(osd, row++, XOSD_string, val);
64         }
65       else if (!strcmp(key, "duration"))
66         msg->max_light = now + atoi(val);
67       else if (!strcmp(key, "min-duration"))
68         msg->min_light = now + atoi(val);
69
70       line = nl;
71     }
72
73   if (msg->min_light > msg->max_light)
74     msg->min_light = msg->max_light;
75 }
76
77 static void
78 hide_msg(struct msg *msg)
79 {
80   xosd_scroll(osd, MAX_LINES);
81   xosd_hide(osd);
82   free(msg);
83 }
84
85 /*** The message queue ***/
86
87 static struct msg *current_msg, *first_msg, *last_msg;
88
89 static void
90 enqueue_msg(unsigned char *buf, int len)
91 {
92   DBG("[%.*s]\n", len, buf);
93   if (!len || buf[len-1] != '\n')
94     return;
95
96   struct msg *msg = xmalloc(sizeof(*msg) + len);
97   memcpy(msg->text, buf, len);
98   msg->text[len] = 0;
99
100   if (first_msg)
101     last_msg->next = msg;
102   else
103     first_msg = msg;
104   last_msg = msg;
105   msg->next = NULL;
106 }
107
108 static void
109 parse_input(unsigned char *buf, int len)
110 {
111   /* The property might contain several messages concatenated. Split them. */
112   while (len > 0)
113     {
114       if (buf[0] == '\n')
115         {
116           buf++, len--;
117           continue;
118         }
119       int i = 0;
120       while (i < len && (buf[i] != '\n' || (i && buf[i-1] != '\n')))
121         i++;
122       enqueue_msg(buf, i);
123       buf += i, len -= i;
124     }
125 }
126
127 /*** Main loop ***/
128
129 int
130 main(void)
131 {
132   XInitThreads();
133
134   Display *dpy = XOpenDisplay(NULL);
135   if (!dpy)
136     die("Cannot open display");
137   Window win = DefaultRootWindow(dpy);
138
139   Atom pty = XInternAtom(dpy, "OSD_QUEUE", False);
140   if (!pty)
141     die("Cannot intern OSD_QUEUE atom");
142
143   XSelectInput(dpy, win, PropertyChangeMask);
144   XDeleteProperty(dpy, win, pty);
145   XFlush(dpy);
146
147   osd = xosd_create(4);
148   if (!osd)
149     die("Cannot initialize OSD");
150   xosd_set_font(osd, "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*");
151   xosd_set_outline_offset(osd, 2);
152   xosd_set_outline_colour(osd, "black");
153   xosd_set_pos(osd, XOSD_middle);
154   xosd_set_align(osd, XOSD_center);
155
156   struct pollfd pfd = {
157     .fd = ConnectionNumber(dpy),
158     .events = POLLIN,
159   };
160
161   for (;;)
162     {
163       struct timeval tv;
164       gettimeofday(&tv, NULL);
165       now = (timestamp_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
166
167       timestamp_t wait_until = now - 1;
168       if (!current_msg && first_msg)
169         {
170           current_msg = first_msg;
171           first_msg = first_msg->next;
172           display_msg(current_msg);
173         }
174       if (current_msg)
175         {
176           if (first_msg)
177             wait_until = current_msg->min_light;
178           else
179             wait_until = current_msg->max_light;
180           if (wait_until <= now)
181             {
182               hide_msg(current_msg);
183               current_msg = NULL;
184               continue;
185             }
186         }
187
188       DBG("Waiting for %d ms\n", (int)(wait_until - now));
189       poll(&pfd, 1, wait_until - now);
190       if (pfd.revents & POLLIN)
191         {
192           while (XPending(dpy))
193             {
194               XEvent ev;
195               XNextEvent(dpy, &ev);
196               if (ev.type != PropertyNotify)
197                 continue;
198               XPropertyEvent *p = &ev.xproperty;
199               if (p->window == win && p->atom == pty)
200                 {
201                   Atom pty_type;
202                   int pty_fmt;
203                   unsigned long pty_items, pty_remains;
204                   unsigned char *pty_buf = NULL;
205                   XGetWindowProperty(dpy, win, pty, 0, 4096, True, XA_STRING, &pty_type, &pty_fmt, &pty_items, &pty_remains, &pty_buf);
206                   if (pty_type == XA_STRING && pty_fmt == 8 && pty_items)
207                     {
208                       DBG("Received: <%.*s>\n", (int) pty_items, pty_buf);
209                       parse_input(pty_buf, pty_items);
210                     }
211                   if (pty_buf)
212                     XFree(pty_buf);
213                 }
214             }
215         }
216     }
217 }