2 * On-screen Display Daemon
4 * (c) 2010--2014 Martin Mares <mj@ucw.cz>
15 #include <X11/Xatom.h>
21 static struct osd_state *osd;
23 static timestamp_t now;
27 static char *font_name = "times-64:bold";
28 static char *default_color = "green";
29 static char *default_outline_color = "black";
30 static int default_outline_width = 2;
31 static int default_duration = 1000;
32 static int default_min_duration = 250;
33 static int debug_mode;
35 static double line_spacing = 0.2;
37 static const char short_opts[] = "c:d:Df:m:o:O:s:";
43 static const struct option long_opts[] = {
44 { "color", required_argument, NULL, 'c' },
45 { "debug", no_argument, NULL, 'D' },
46 { "duration", required_argument, NULL, 'd' },
47 { "font", required_argument, NULL, 'f' },
48 { "min-duration", required_argument, NULL, 'm' },
49 { "outline-color", required_argument, NULL, 'o' },
50 { "outline-width", required_argument, NULL, 'O' },
51 { "line-spacing", required_argument, NULL, 's' },
52 { "test", no_argument, NULL, OPT_TEST }, // Undocumented test mode
59 fprintf(stderr, "Usage: osdd <options>\n\n\
61 -c, --color=<c>\t\tDefault color (#rgb, #rrggbb or a name from rgb.txt)\n\
62 -D, --debug\t\tDebugging mode (do not detach from the terminal)\n\
63 -d, --duration=<ms>\tDefault message duration in milliseconds\n\
64 -f, --font=<f>\t\tFont to use for the OSD\n\
65 -m, --min-duration=<ms>\tDefault minimum message duration in milliseconds\n\
66 -o, --outline-color=<c>\tDefault outline color\n\
67 -O, --outline-width=<n>\tDefault outline width (default=2)\n\
68 -s, --line-spacing=<n>\tSet line spacing factor (decimal fraction, default=0.2)\n\
74 parse_opts(int argc, char **argv)
77 while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
81 default_color = optarg;
84 default_duration = atoi(optarg);
93 line_spacing = atof(optarg);
96 default_min_duration = atoi(optarg);
99 default_outline_color = optarg;
102 default_outline_width = atoi(optarg);
116 /*** Displaying of messages ***/
120 timestamp_t min_light, max_light;
125 display_msg(struct msg *msg)
127 DBG("## Displaying message\n");
128 msg->min_light = now + default_min_duration;
129 msg->max_light = now + default_duration;
130 char *fg_color = default_color;
131 char *outline_color = default_outline_color;
132 int outline_width = default_outline_width;
134 char *line = msg->text;
137 // The parser it destructive, but it does not do any harm, since we display each message only once.
138 char *nl = strchr(line, '\n');
142 char *val = strchr(line, ':');
153 DBG("\t%s:%s\n", key, val);
155 struct osd_line *l = NULL;
158 l = osd_add_line(osd, OSD_TYPE_TEXT);
159 sprintf(l->u.text, "%.*s", OSD_MAX_LINE_LEN, val);
161 else if (!strcmp(key, "percentage") || !strcmp(key, "percent"))
163 l = osd_add_line(osd, OSD_TYPE_PERCENTAGE);
164 l->u.percent = atoi(val);
166 else if (!strcmp(key, "slider"))
168 l = osd_add_line(osd, OSD_TYPE_SLIDER);
169 l->u.percent = atoi(val);
171 else if (!strcmp(key, "duration"))
172 msg->max_light = now + atoi(val);
173 else if (!strcmp(key, "min-duration"))
174 msg->min_light = now + atoi(val);
175 else if (!strcmp(key, "color"))
177 else if (!strcmp(key, "outline-color"))
179 else if (!strcmp(key, "outline-width"))
180 outline_width = atoi(val);
184 l->fg_color = fg_color;
185 l->outline_color = outline_color;
186 l->outline_width = outline_width;
192 if (msg->min_light > msg->max_light)
193 msg->min_light = msg->max_light;
199 hide_msg(struct msg *msg)
201 DBG("## Hiding message\n");
206 /*** The message queue ***/
208 static struct msg *current_msg, *first_msg, *last_msg;
211 enqueue_msg(unsigned char *buf, int len)
213 DBG("Received: [%.*s]\n", len, buf);
214 if (!len || buf[len-1] != '\n')
217 struct msg *msg = xmalloc(sizeof(*msg) + len);
218 memcpy(msg->text, buf, len);
222 last_msg->next = msg;
230 parse_input(unsigned char *buf, int len)
232 /* The property might contain several messages concatenated. Split them. */
241 while (i < len && (buf[i] != '\n' || (i && buf[i-1] != '\n')))
251 unsigned char buf[4096];
255 while ((c = read(0, buf + len, 4096 - len)) > 0)
258 enqueue_msg(buf, len);
264 main(int argc, char **argv)
266 parse_opts(argc, argv);
267 setlocale(LC_CTYPE, "");
270 Display *dpy = XOpenDisplay(NULL);
272 die("Cannot open display");
273 Window win = DefaultRootWindow(dpy);
275 Atom pty = XInternAtom(dpy, "OSD_QUEUE", False);
277 die("Cannot intern OSD_QUEUE atom");
283 die("Cannot fork: %m");
296 XSelectInput(dpy, win, PropertyChangeMask);
297 XDeleteProperty(dpy, win, pty);
302 osd_set_font(osd, font_name, line_spacing);
304 struct pollfd pfd = {
305 .fd = ConnectionNumber(dpy),
311 now = get_current_time();
313 timestamp_t wait_until = now - 1;
314 if (!current_msg && first_msg)
316 current_msg = first_msg;
317 first_msg = first_msg->next;
318 display_msg(current_msg);
323 wait_until = current_msg->min_light;
325 wait_until = current_msg->max_light;
326 if (wait_until <= now)
328 hide_msg(current_msg);
333 if (test_mode && !current_msg)
336 DBG("... waiting for %d ms\n", (int)(wait_until - now));
337 poll(&pfd, 1, wait_until - now);
338 if (pfd.revents & POLLIN)
340 while (XPending(dpy))
343 XNextEvent(dpy, &ev);
344 if (osd_handle_event(osd, &ev))
346 if (ev.type != PropertyNotify)
348 XPropertyEvent *p = &ev.xproperty;
349 if (p->window == win && p->atom == pty)
353 unsigned long pty_items, pty_remains;
354 unsigned char *pty_buf = NULL;
355 XGetWindowProperty(dpy, win, pty, 0, 4096, True, XA_STRING, &pty_type, &pty_fmt, &pty_items, &pty_remains, &pty_buf);
356 if (pty_type == XA_STRING && pty_fmt == 8 && pty_items)
357 parse_input(pty_buf, pty_items);