/*
* 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 <getopt.h>
-#include <sys/time.h>
+#include <locale.h>
#include <xosd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#undef DEBUG
#include "util.h"
+#include "display.h"
-static xosd *osd;
+static struct osd_state *osd;
-typedef uint64_t timestamp_t;
static timestamp_t now;
/*** Options ***/
-static int num_lines = 4;
-static char *font_name = "-bitstream-bitstream vera sans-bold-r-normal-*-*-320-*-*-p-*-*";
+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 = 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:f:l:m:o:";
+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' },
- { "lines", required_argument, NULL, 'l' },
{ "min-duration", required_argument, NULL, 'm' },
{ "outline-color", required_argument, NULL, 'o' },
- { NULL, 0, NULL, 0 },
+ { "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
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\
--l, --lines=<n>\t\tNumber of lines of 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);
}
case 'd':
default_duration = atoi(optarg);
break;
+ case 'D':
+ debug_mode = 1;
+ break;
case 'f':
font_name = optarg;
break;
case 'l':
- num_lines = atoi(optarg);
- if (num_lines < 1)
- usage();
+ line_spacing = atof(optarg);
break;
case 'm':
default_min_duration = atoi(optarg);
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();
}
DBG("## Displaying message\n");
msg->min_light = now + default_min_duration;
msg->max_light = now + default_duration;
- xosd_set_colour(osd, default_color);
- xosd_set_outline_colour(osd, default_outline_color);
+ 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 do any harm, since we display each message only once.
}
DBG("\t%s:%s\n", key, val);
+ struct osd_line *l = NULL;
if (!key[0])
{
- if (row < num_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"))
{
- if (row < num_lines)
- xosd_display(osd, row++, XOSD_percentage, atoi(val));
+ l = osd_add_line(osd, OSD_TYPE_PERCENTAGE);
+ l->u.percent = atoi(val);
}
else if (!strcmp(key, "slider"))
{
- if (row < num_lines)
- xosd_display(osd, row++, XOSD_slider, atoi(val));
+ 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"))
- xosd_set_colour(osd, val);
+ fg_color = val;
else if (!strcmp(key, "outline-color"))
- xosd_set_outline_colour(osd, val);
- else
- xosd_display(osd, (row < num_lines ? row++ : num_lines-1), XOSD_string, "PARSE ERROR");
+ 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)
{
DBG("## Hiding message\n");
- for (int i=0; i<num_lines; i++)
- xosd_display(osd, i, XOSD_string, "");
- xosd_hide(osd);
+ osd_clear(osd);
free(msg);
}
}
}
+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(int argc, char **argv)
{
parse_opts(argc, argv);
+ setlocale(LC_CTYPE, "");
XInitThreads();
Display *dpy = XOpenDisplay(NULL);
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();
+ }
+
+ if (test_mode)
+ {
+ do_test();
+ pty = 0;
+ }
+ else
+ {
+ XSelectInput(dpy, win, PropertyChangeMask);
+ XDeleteProperty(dpy, win, pty);
+ XFlush(dpy);
+ }
- osd = xosd_create(num_lines);
- if (!osd)
- die("Cannot initialize OSD");
- xosd_set_font(osd, font_name);
- xosd_set_outline_offset(osd, 2);
- xosd_set_pos(osd, XOSD_middle);
- xosd_set_align(osd, XOSD_center);
+ osd = osd_new(dpy);
+ osd_set_font(osd, font_name, line_spacing);
struct pollfd pfd = {
.fd = ConnectionNumber(dpy),
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)
continue;
}
}
+ if (test_mode && !current_msg)
+ break;
DBG("... waiting for %d ms\n", (int)(wait_until - now));
poll(&pfd, 1, wait_until - now);
{
XEvent ev;
XNextEvent(dpy, &ev);
+ if (osd_handle_event(osd, &ev))
+ continue;
if (ev.type != PropertyNotify)
continue;
XPropertyEvent *p = &ev.xproperty;