+ rethink_display(notify);
+}
+
+#ifdef CONFIG_X11
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+static Display *x11_dpy;
+static unsigned leds_care, leds_have, leds_want;
+
+static Atom osd_pty;
+static unsigned osd_care;
+#define OSD_MSG_SIZE 1024
+static char osd_last_msg[OSD_MSG_SIZE];
+static time_t osd_last_time;
+
+static void
+x11_init(void)
+{
+ leds_care = (global_options.led >= 0 ? (1 << global_options.led) : 0);
+ osd_care = (global_options.osd >= 0);
+ CLIST_FOR_EACH(struct option_node *, o, options)
+ {
+ if (o->o.led > 0)
+ leds_care |= (1 << o->o.led);
+ if (o->o.osd > 0)
+ osd_care = 1;
+ }
+
+ if (!leds_care && !osd_care)
+ {
+ debug("X11: No mailbox wants LEDs or OSD\n");
+ return;
+ }
+ if (!getenv("DISPLAY"))
+ {
+ debug("X11: Do not have X display\n");
+ return;
+ }
+ if (!(x11_dpy = XOpenDisplay(NULL)))
+ die("Cannot open X display, although the DISPLAY variable is set");
+
+ if (osd_care)
+ {
+ osd_pty = XInternAtom(x11_dpy, "OSD_QUEUE", False);
+ if (!osd_pty)
+ die("Cannot intern OSD_QUEUE atom");
+
+ // If OSD options contain no message, add one
+ int seen_msg = 0;
+ CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+ if (!n->key[0])
+ seen_msg = 1;
+ if (!seen_msg)
+ {
+ add_osd_opt("=%40f");
+ add_osd_opt("=%40s");
+ add_osd_opt("=");
+ add_osd_opt("=(and %m more)");
+ }
+ }
+
+ leds_have = ~0U;
+ debug("X11: Initialized\n");
+}
+
+static void
+sync_leds(void)
+{
+ if (leds_want == leds_have)
+ return;
+
+ debug("LEDS: have %02x, want %02x, care %02x\n", leds_have, leds_want, leds_care);
+ for (int i=1; i<10; i++)
+ if (leds_care & (leds_have ^ leds_want) & (1 << i))
+ {
+ XKeyboardControl cc;
+ cc.led = i;
+ cc.led_mode = (leds_want & (1 << i)) ? LedModeOn : LedModeOff;
+ XChangeKeyboardControl(x11_dpy, KBLed | KBLedMode, &cc);
+ }
+ XFlush(x11_dpy);
+ leds_have = leds_want;
+}
+
+static void
+rethink_leds(void)
+{
+ if (!leds_care || !x11_dpy)
+ return;
+
+ leds_want = 0;
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ if (b->o.led > 0 && b->new)
+ leds_want |= (1 << b->o.led);
+ sync_leds();
+}
+
+struct osd_params {
+ struct mbox *mbox;
+ int total_new;
+};
+
+static int
+format_osd_string(char *dest, char *src, struct osd_params *par)
+{
+ char *stop = dest + OSD_MSG_SIZE - 1;
+ char numbuf[16];
+
+ while (*src && dest < stop)
+ {
+ if (*src == '%')
+ {
+ src++;
+ int size = 0;
+ while (*src >= '0' && *src <= '9')
+ size = 10*size + *src++ - '0';
+ if (!size || size > stop-dest)
+ size = dest-stop;
+
+ int spec = *src++;
+ if (!spec)
+ break;
+
+ char *arg = numbuf;
+ switch (spec)
+ {
+ case 'f':
+ arg = par->mbox->sender_snippet;
+ break;
+ case 's':
+ arg = par->mbox->subject_snippet;
+ break;
+ case 'n':
+ snprintf(numbuf, sizeof(numbuf), "%d", par->total_new);
+ break;
+ case 'm':
+ if (par->total_new < 2)
+ return 0;
+ snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1);
+ break;
+ case '%':
+ arg = "%";
+ break;
+ default:
+ arg = "???";
+ break;
+ }
+
+ while (*arg && size)
+ {
+ *dest++ = *arg++;
+ size--;
+ }
+ }
+ else
+ *dest++ = *src++;
+ }
+ *dest = 0;
+ return 1;
+}
+
+static void
+format_osd(char *msg, struct osd_params *par)
+{
+ if (!par->mbox)
+ {
+ msg[0] = 0;
+ return;
+ }
+
+ unsigned pos = 0;
+ unsigned have_text = 0;
+ CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts)
+ {
+ char buf[OSD_MSG_SIZE];
+ if (!format_osd_string(buf, n->val, par))
+ continue;
+ if (!n->key[0] && buf[0])
+ have_text = 1;
+ pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf);
+ if (pos > OSD_MSG_SIZE-1)
+ {
+ pos = sprintf(msg, "OSD message too long!\n");
+ break;
+ }
+ }
+ if (have_text)
+ msg[pos++] = '\n';
+ else
+ pos = 0;
+ msg[pos] = 0;
+}
+
+static void
+debug_osd_msg(char *msg)
+{
+ if (!debug_mode)
+ return;
+ fprintf(stderr, "OSD: <");
+ while (*msg)
+ {
+ fputc((*msg != '\n' ? *msg : '|'), stderr);
+ msg++;
+ }
+ fprintf(stderr, ">\n");
+}
+
+static void
+rethink_osd(int notify)
+{
+ if (!osd_care || !x11_dpy || !allow_osd)
+ {
+ osd_last_msg[0] = 0;
+ return;
+ }
+
+ struct osd_params p = { .mbox = NULL, .total_new = 0 };
+ CLIST_FOR_EACH(struct mbox *, b, mboxes)
+ if (b->o.osd > 0)
+ {
+ p.total_new += b->new;
+ if (b->new && b->last_time > osd_last_time && (!p.mbox || p.mbox->o.priority < b->o.priority))
+ p.mbox = b;
+ }
+
+ char new_msg[OSD_MSG_SIZE];
+ format_osd(new_msg, &p);
+ debug_osd_msg(new_msg);
+ if (strcmp(new_msg, osd_last_msg))
+ {
+ strcpy(osd_last_msg, new_msg);
+ if (notify && new_msg[0])
+ {
+ debug("OSD: Sending to daemon\n");
+ XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg));
+ XFlush(x11_dpy);
+ }
+ else
+ debug("OSD: No changes\n");
+ osd_last_time = time(NULL);
+ }