9 #include <X11/extensions/shape.h>
10 #include <X11/extensions/render.h>
11 #include <X11/Xft/Xft.h>
37 stay_on_top(struct osd_state *osd)
39 /* Heavily inspired by a similar function in libxosd */
40 Window root = DefaultRootWindow(osd->dpy);
42 unsigned long nitems, bytes_after;
43 unsigned char *prop = NULL;
46 // Gnome-compliant way
47 Atom gnome = XInternAtom(osd->dpy, "_WIN_SUPPORTING_WM_CHECK", False);
48 if (XGetWindowProperty(osd->dpy, root, gnome, 0, 16384, False, AnyPropertyType,
49 &type, &format, &nitems, &bytes_after, &prop) == Success &&
52 DBG("stay_on_top: Gnome mode\n");
53 // FIXME: check capabilities
54 XClientMessageEvent e;
55 memset(&e, 0, sizeof(e));
56 e.type = ClientMessage;
58 e.message_type = XInternAtom(osd->dpy, "_WIN_LAYER", False);
60 e.data.l[0] = 6; // WIN_LAYER_ONTOP */
61 XSendEvent(osd->dpy, root, False, SubstructureNotifyMask, (XEvent *) &e);
66 // NetWM-compliant way
67 Atom net_wm = XInternAtom(osd->dpy, "_NET_SUPPORTED", False);
68 if (XGetWindowProperty(osd->dpy, root, net_wm, 0, 16384, False, AnyPropertyType,
69 &type, &format, &nitems, &bytes_after, &prop) == Success &&
72 DBG("stay_on_top: NetWM mode\n");
74 memset(&e, 0, sizeof(e));
75 e.xclient.type = ClientMessage;
76 e.xclient.message_type = XInternAtom(osd->dpy, "_NET_WM_STATE", False);
77 e.xclient.display = osd->dpy;
78 e.xclient.window = osd->win;
79 e.xclient.format = 32;
80 e.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
81 e.xclient.data.l[1] = XInternAtom(osd->dpy, "_NET_WM_STATE_STAYS_ON_TOP", False);
82 XSendEvent(osd->dpy, root, False, SubstructureRedirectMask, &e);
87 DBG("stay_on_top: WM does not support any known protocol\n");
91 main(int argc, char **argv)
95 struct osd_state osd_static;
96 bzero(&osd_static, sizeof(osd_static));
97 struct osd_state *osd = &osd_static;
99 osd->dpy = XOpenDisplay(NULL);
101 die("Cannot open display");
103 int event_basep, error_basep;
104 if (!XShapeQueryExtension(osd->dpy, &event_basep, &error_basep))
105 die("XShape extension not supported by X server, giving up");
107 osd->screen = XDefaultScreen(osd->dpy);
108 osd->visual = XDefaultVisual(osd->dpy, osd->screen);
109 osd->depth = XDefaultDepth(osd->dpy, osd->screen);
110 osd->cmap = DefaultColormap(osd->dpy, osd->screen);
112 // These can change. And what about Xinerama?
113 osd->screen_width = XDisplayWidth(osd->dpy, osd->screen);
114 osd->screen_height = XDisplayHeight(osd->dpy, osd->screen);
116 XSetWindowAttributes win_attr = {
117 .override_redirect = 1,
119 osd->win = XCreateWindow(osd->dpy,
120 XRootWindow(osd->dpy, osd->screen),
122 osd->screen_width, osd->screen_height,
129 XStoreName(osd->dpy, osd->win, "OSD");
133 osd->mask_bitmap = XCreatePixmap(osd->dpy, osd->win, osd->screen_width, osd->screen_height, 1);
134 osd->image_pixmap = XCreatePixmap(osd->dpy, osd->win, osd->screen_width, osd->screen_height, osd->depth);
135 DBG("depth = %d\n", osd->depth);
138 .graphics_exposures = 0,
140 osd->gc = XCreateGC(osd->dpy, osd->win, GCGraphicsExposures, &gcv);
141 osd->mask_gc = XCreateGC(osd->dpy, osd->mask_bitmap, GCGraphicsExposures, &gcv);
143 XSetBackground(osd->dpy, osd->gc, BlackPixel(osd->dpy, osd->screen));
144 XColor shadow_color = { .red = 0xffff, .green = 0xffff, .blue = 0 };
145 XAllocColor(osd->dpy, osd->cmap, &shadow_color);
146 XSetForeground(osd->dpy, osd->gc, shadow_color.pixel);
148 XSetBackground(osd->dpy, osd->mask_gc, WhitePixel(osd->dpy, osd->screen));
149 XSetForeground(osd->dpy, osd->mask_gc, BlackPixel(osd->dpy, osd->screen));
151 XFillRectangle(osd->dpy, osd->image_pixmap, osd->gc, 0, 0, osd->screen_width, osd->screen_height);
153 XFillRectangle(osd->dpy, osd->mask_bitmap, osd->mask_gc, 0, 0, osd->screen_width, osd->screen_height);
155 osd->font = XftFontOpenName(osd->dpy, osd->screen, "times-64");
157 die("Cannot open font");
158 DBG("Font: asc=%d desc=%d ht=%d\n", osd->font->ascent, osd->font->descent, osd->font->height);
160 osd->mask_draw = XftDrawCreateBitmap(osd->dpy, osd->mask_bitmap);
162 die("Cannot create XftDraw");
164 osd->image_draw = XftDrawCreate(osd->dpy, osd->image_pixmap, osd->visual, osd->cmap);
165 if (!osd->image_draw)
166 die("Cannot create XftDraw");
168 XRenderColor xrc = { .red = 0, .green = 0xffff, .blue = 0, .alpha = 0xffff };
170 if (!XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &xrc, &xfc))
171 die("XftColorAllocValue failed");
172 const unsigned char str[] = "Žluťoučká vlkodlačice";
173 XftDrawStringUtf8(osd->image_draw, &xfc, osd->font, 100, 100, str, strlen((char *) str));
175 XRenderColor xrcm = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
177 if (!XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &xrcm, &xfcm))
178 die("XftColorAllocValue failed");
180 // This is slow, but unlike the method used by libxosd, the result isn't ugly.
182 for (int dx = -outline; dx <= outline; dx++)
183 for (int dy = -outline; dy <= outline; dy++)
184 if (dx*dx + dy*dy <= outline*outline)
185 XftDrawStringUtf8(osd->mask_draw, &xfcm, osd->font, 100 + dx, 100 + dy, str, strlen((char *) str));
189 XftTextExtentsUtf8(osd->dpy, osd->font, str, strlen((char *) str), &gi);
190 DBG("Glyph info: (%d,%d)+(%d,%d) off (%d,%d)\n", gi.x, gi.y, gi.width, gi.height, gi.xOff, gi.yOff);
191 XftDrawRect(osd->image_draw, &xfc, 100 + gi.x, 100 - gi.y, gi.width, gi.height);
194 XShapeCombineMask(osd->dpy, osd->win, ShapeBounding, 0, 0, osd->mask_bitmap, ShapeSet);
196 XSelectInput(osd->dpy, osd->win, ExposureMask);
197 XMapRaised(osd->dpy, osd->win);
200 struct pollfd pfd = {
201 .fd = ConnectionNumber(osd->dpy),
207 timestamp_t now = get_current_time();
208 timestamp_t wait_until = now - 1;
210 DBG("... waiting for %d ms\n", (int)(wait_until - now));
211 poll(&pfd, 1, wait_until - now);
212 if (pfd.revents & POLLIN)
214 while (XPending(osd->dpy))
217 XNextEvent(osd->dpy, &ev);
222 XExposeEvent *ex = &ev.xexpose;
223 DBG("Expose cnt=%d (%d,%d)+(%d,%d)\n", ex->count, ex->x, ex->y, ex->width, ex->height);
224 XCopyArea(osd->dpy, osd->image_pixmap, osd->win, osd->gc, ex->x, ex->y, ex->width, ex->height, ex->x, ex->y);
228 DBG("Event %d\n", ev.type);