--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+#include <getopt.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/render.h>
+#include <X11/Xft/Xft.h>
+
+#define DEBUG
+#include "osd.h"
+
+struct osd_state {
+ Display *dpy;
+ int screen;
+ Visual *visual;
+ Colormap cmap;
+ int depth;
+ int screen_width;
+ int screen_height;
+ Window win;
+ Pixmap mask_bitmap;
+ Pixmap image_pixmap;
+ GC gc;
+ GC mask_gc;
+
+ // Xft stuff
+ XftFont *font;
+ XftDraw *xft_draw;
+};
+
+int
+main(int argc, char **argv)
+{
+ XInitThreads();
+
+ struct osd_state osd_static;
+ bzero(&osd_static, sizeof(osd_static));
+ struct osd_state *osd = &osd_static;
+
+ osd->dpy = XOpenDisplay(NULL);
+ if (!osd->dpy)
+ die("Cannot open display");
+
+ int event_basep, error_basep;
+ if (!XShapeQueryExtension(osd->dpy, &event_basep, &error_basep))
+ die("XShape extension not supported by X server, giving up");
+
+ osd->screen = XDefaultScreen(osd->dpy);
+ osd->visual = XDefaultVisual(osd->dpy, osd->screen);
+ osd->depth = XDefaultDepth(osd->dpy, osd->screen);
+ osd->cmap = DefaultColormap(osd->dpy, osd->screen);
+
+ // These can change. And what about Xinerama?
+ osd->screen_width = XDisplayWidth(osd->dpy, osd->screen);
+ osd->screen_height = XDisplayHeight(osd->dpy, osd->screen);
+
+ XSetWindowAttributes win_attr = {
+ .override_redirect = 0, // FIXME: 1
+ };
+ osd->win = XCreateWindow(osd->dpy,
+ XRootWindow(osd->dpy, osd->screen),
+ 0, 0,
+ osd->screen_width, osd->screen_height,
+ 0,
+ osd->depth,
+ CopyFromParent,
+ osd->visual,
+ CWOverrideRedirect,
+ &win_attr);
+ XStoreName(osd->dpy, osd->win, "OSD");
+
+ // FIXME: Size
+ osd->mask_bitmap = XCreatePixmap(osd->dpy, osd->win, osd->screen_width, osd->screen_height, 1);
+ osd->image_pixmap = XCreatePixmap(osd->dpy, osd->win, osd->screen_width, osd->screen_height, osd->depth);
+ DBG("depth = %d\n", osd->depth);
+
+ XGCValues gcv = {
+ .graphics_exposures = 0,
+ };
+ osd->gc = XCreateGC(osd->dpy, osd->win, GCGraphicsExposures, &gcv);
+ osd->mask_gc = XCreateGC(osd->dpy, osd->mask_bitmap, GCGraphicsExposures, &gcv);
+
+ XSetBackground(osd->dpy, osd->gc, BlackPixel(osd->dpy, osd->screen));
+ XSetForeground(osd->dpy, osd->gc, BlackPixel(osd->dpy, osd->screen));
+
+ XSetBackground(osd->dpy, osd->mask_gc, WhitePixel(osd->dpy, osd->screen));
+ XSetForeground(osd->dpy, osd->mask_gc, BlackPixel(osd->dpy, osd->screen));
+
+ // FIXME: Stay on top
+
+ XFillRectangle(osd->dpy, osd->image_pixmap, osd->gc, 0, 0, osd->screen_width, osd->screen_height);
+
+ osd->font = XftFontOpenName(osd->dpy, osd->screen, "times-64");
+ if (!osd->font)
+ die("Cannot open font");
+
+ osd->xft_draw = XftDrawCreate(osd->dpy, osd->image_pixmap, osd->visual, osd->cmap);
+ if (!osd->xft_draw)
+ die("Cannot create XftDraw");
+
+ XRenderColor xrc = { .red = 0, .green = 0xffff, .blue = 0, .alpha = 0xffff };
+ XftColor xfc;
+ if (!XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &xrc, &xfc))
+ die("XftColorAllocValue failed");
+ const unsigned char str[] = "Žluťoučká vlkodlačice";
+ XftDrawStringUtf8(osd->xft_draw, &xfc, osd->font, 100, 100, str, strlen((char *) str));
+
+ XGlyphInfo gi;
+ XftTextExtentsUtf8(osd->dpy, osd->font, str, strlen((char *) str), &gi);
+ DBG("Glyph info: (%d,%d)+(%d,%d) off (%d,%d)\n", gi.x, gi.y, gi.width, gi.height, gi.xOff, gi.yOff);
+ XftDrawRect(osd->xft_draw, &xfc, 100 + gi.x, 100 - gi.y, gi.width, gi.height);
+
+ XftDrawRect(osd->xft_draw, &xfc, 30, 30, 50, 50);
+
+ XSelectInput(osd->dpy, osd->win, ExposureMask);
+ XMapRaised(osd->dpy, osd->win);
+ XFlush(osd->dpy);
+
+ struct pollfd pfd = {
+ .fd = ConnectionNumber(osd->dpy),
+ .events = POLLIN,
+ };
+
+ for (;;)
+ {
+ timestamp_t now = get_current_time();
+ timestamp_t wait_until = now - 1;
+
+ DBG("... waiting for %d ms\n", (int)(wait_until - now));
+ poll(&pfd, 1, wait_until - now);
+ if (pfd.revents & POLLIN)
+ {
+ while (XPending(osd->dpy))
+ {
+ XEvent ev;
+ XNextEvent(osd->dpy, &ev);
+ switch (ev.type)
+ {
+ case Expose:
+ {
+ XExposeEvent *ex = &ev.xexpose;
+ DBG("Expose cnt=%d (%d,%d)+(%d,%d)\n", ex->count, ex->x, ex->y, ex->width, ex->height);
+ XCopyArea(osd->dpy, osd->image_pixmap, osd->win, osd->gc, ex->x, ex->y, ex->width, ex->height, ex->x, ex->y);
+ break;
+ }
+ default:
+ DBG("Event %d\n", ev.type);
+ }
+ }
+ }
+ }
+
+ // FIXME: Cleanup
+}