2 * On-screen Display Daemon
4 * (c) 2010 Martin Mares <mj@ucw.cz>
16 #include <X11/Xatom.h>
23 static timestamp_t now;
27 static int num_lines = 4;
28 static char *font_name = "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*";
29 static char *default_color = "green";
30 static char *default_outline_color = "black";
31 static int default_duration = 1000;
32 static int default_min_duration = 250;
33 static int debug_mode;
35 static const char short_opts[] = "c:d:Df:l:m:o:";
37 static const struct option long_opts[] = {
38 { "color", required_argument, NULL, 'c' },
39 { "debug", no_argument, NULL, 'D' },
40 { "duration", required_argument, NULL, 'd' },
41 { "font", required_argument, NULL, 'f' },
42 { "lines", required_argument, NULL, 'l' },
43 { "min-duration", required_argument, NULL, 'm' },
44 { "outline-color", required_argument, NULL, 'o' },
51 fprintf(stderr, "Usage: osdd <options>\n\n\
53 -c, --color=<c>\t\tDefault color (#rgb, #rrggbb or a name from rgb.txt)\n\
54 -D, --debug\t\tDebugging mode (do not detach from the terminal)\n\
55 -d, --duration=<ms>\tDefault message duration in milliseconds\n\
56 -f, --font=<f>\t\tFont to use for the OSD\n\
57 -l, --lines=<n>\t\tNumber of lines of the OSD\n\
58 -m, --min-duration=<ms>\tDefault minimum message duration in milliseconds\n\
59 -o, --outline-color=<c>\tDefault outline color\n\
65 parse_opts(int argc, char **argv)
68 while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
72 default_color = optarg;
75 default_duration = atoi(optarg);
84 num_lines = atoi(optarg);
89 default_min_duration = atoi(optarg);
92 default_outline_color = optarg;
102 /*** Displaying of messages ***/
106 timestamp_t min_light, max_light;
111 display_msg(struct msg *msg)
113 DBG("## Displaying message\n");
114 msg->min_light = now + default_min_duration;
115 msg->max_light = now + default_duration;
116 xosd_set_colour(osd, default_color);
117 xosd_set_outline_colour(osd, default_outline_color);
119 char *line = msg->text;
123 // The parser it destructive, but it does not do any harm, since we display each message only once.
124 char *nl = strchr(line, '\n');
128 char *val = strchr(line, ':');
139 DBG("\t%s:%s\n", key, val);
144 xosd_display(osd, row++, XOSD_string, val);
146 else if (!strcmp(key, "percentage") || !strcmp(key, "percent"))
149 xosd_display(osd, row++, XOSD_percentage, atoi(val));
151 else if (!strcmp(key, "slider"))
154 xosd_display(osd, row++, XOSD_slider, atoi(val));
156 else if (!strcmp(key, "duration"))
157 msg->max_light = now + atoi(val);
158 else if (!strcmp(key, "min-duration"))
159 msg->min_light = now + atoi(val);
160 else if (!strcmp(key, "color"))
161 xosd_set_colour(osd, val);
162 else if (!strcmp(key, "outline-color"))
163 xosd_set_outline_colour(osd, val);
165 xosd_display(osd, (row < num_lines ? row++ : num_lines-1), XOSD_string, "PARSE ERROR");
170 if (msg->min_light > msg->max_light)
171 msg->min_light = msg->max_light;
175 hide_msg(struct msg *msg)
177 DBG("## Hiding message\n");
178 for (int i=0; i<num_lines; i++)
179 xosd_display(osd, i, XOSD_string, "");
184 /*** The message queue ***/
186 static struct msg *current_msg, *first_msg, *last_msg;
189 enqueue_msg(unsigned char *buf, int len)
191 DBG("Received: [%.*s]\n", len, buf);
192 if (!len || buf[len-1] != '\n')
195 struct msg *msg = xmalloc(sizeof(*msg) + len);
196 memcpy(msg->text, buf, len);
200 last_msg->next = msg;
208 parse_input(unsigned char *buf, int len)
210 /* The property might contain several messages concatenated. Split them. */
219 while (i < len && (buf[i] != '\n' || (i && buf[i-1] != '\n')))
229 main(int argc, char **argv)
231 parse_opts(argc, argv);
232 setlocale(LC_CTYPE, "");
235 Display *dpy = XOpenDisplay(NULL);
237 die("Cannot open display");
238 Window win = DefaultRootWindow(dpy);
240 Atom pty = XInternAtom(dpy, "OSD_QUEUE", False);
242 die("Cannot intern OSD_QUEUE atom");
248 die("Cannot fork: %m");
254 XSelectInput(dpy, win, PropertyChangeMask);
255 XDeleteProperty(dpy, win, pty);
258 osd = xosd_create(num_lines);
260 die("Cannot initialize OSD");
261 xosd_set_font(osd, font_name);
262 xosd_set_outline_offset(osd, 2);
263 xosd_set_pos(osd, XOSD_middle);
264 xosd_set_align(osd, XOSD_center);
266 struct pollfd pfd = {
267 .fd = ConnectionNumber(dpy),
273 now = get_current_time();
275 timestamp_t wait_until = now - 1;
276 if (!current_msg && first_msg)
278 current_msg = first_msg;
279 first_msg = first_msg->next;
280 display_msg(current_msg);
285 wait_until = current_msg->min_light;
287 wait_until = current_msg->max_light;
288 if (wait_until <= now)
290 hide_msg(current_msg);
296 DBG("... waiting for %d ms\n", (int)(wait_until - now));
297 poll(&pfd, 1, wait_until - now);
298 if (pfd.revents & POLLIN)
300 while (XPending(dpy))
303 XNextEvent(dpy, &ev);
304 if (ev.type != PropertyNotify)
306 XPropertyEvent *p = &ev.xproperty;
307 if (p->window == win && p->atom == pty)
311 unsigned long pty_items, pty_remains;
312 unsigned char *pty_buf = NULL;
313 XGetWindowProperty(dpy, win, pty, 0, 4096, True, XA_STRING, &pty_type, &pty_fmt, &pty_items, &pty_remains, &pty_buf);
314 if (pty_type == XA_STRING && pty_fmt == 8 && pty_items)
315 parse_input(pty_buf, pty_items);