4 * (c) 2013 Martin Mares <mj@ucw.cz>
6 * This code is heavily inspired by the libxosd library,
7 * which is (c) 2000, 2001 Andre Renaud <andre@ignavus.net>.
16 #include <X11/Xatom.h>
17 #include <X11/extensions/shape.h>
18 #include <X11/extensions/render.h>
19 #include <X11/Xft/Xft.h>
26 // Basic characteristics of current display and screen
50 // Contents of the display
51 struct osd_line *lines;
60 stay_on_top(struct osd_state *osd)
63 unsigned long nitems, bytes_after;
64 unsigned char *prop = NULL;
67 // Gnome-compliant way
68 Atom gnome = XInternAtom(osd->dpy, "_WIN_SUPPORTING_WM_CHECK", False);
69 if (XGetWindowProperty(osd->dpy, osd->root, gnome, 0, 16384, False, AnyPropertyType,
70 &type, &format, &nitems, &bytes_after, &prop) == Success &&
73 DBG("stay_on_top: Gnome mode\n");
74 // FIXME: check capabilities
75 XClientMessageEvent e;
76 memset(&e, 0, sizeof(e));
77 e.type = ClientMessage;
79 e.message_type = XInternAtom(osd->dpy, "_WIN_LAYER", False);
81 e.data.l[0] = 6; // WIN_LAYER_ONTOP */
82 XSendEvent(osd->dpy, osd->root, False, SubstructureNotifyMask, (XEvent *) &e);
87 // NetWM-compliant way
88 Atom net_wm = XInternAtom(osd->dpy, "_NET_SUPPORTED", False);
89 if (XGetWindowProperty(osd->dpy, osd->root, net_wm, 0, 16384, False, AnyPropertyType,
90 &type, &format, &nitems, &bytes_after, &prop) == Success &&
93 DBG("stay_on_top: NetWM mode\n");
95 memset(&e, 0, sizeof(e));
96 e.xclient.type = ClientMessage;
97 e.xclient.message_type = XInternAtom(osd->dpy, "_NET_WM_STATE", False);
98 e.xclient.display = osd->dpy;
99 e.xclient.window = osd->win;
100 e.xclient.format = 32;
101 e.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
102 e.xclient.data.l[1] = XInternAtom(osd->dpy, "_NET_WM_STATE_STAYS_ON_TOP", False);
103 XSendEvent(osd->dpy, osd->root, False, SubstructureRedirectMask, &e);
108 DBG("stay_on_top: WM does not support any known protocol\n");
111 struct osd_state *osd_new(Display *dpy)
113 struct osd_state *osd = xmalloc(sizeof(*osd));
114 memset(osd, 0, sizeof(*osd));
117 osd->screen = XDefaultScreen(osd->dpy);
118 osd->visual = XDefaultVisual(osd->dpy, osd->screen);
119 osd->cmap = DefaultColormap(osd->dpy, osd->screen);
120 osd->root = DefaultRootWindow(osd->dpy);
122 // FIXME: These can change. And what about Xinerama?
123 osd->depth = XDefaultDepth(osd->dpy, osd->screen);
124 osd->screen_width = XDisplayWidth(osd->dpy, osd->screen);
125 osd->screen_height = XDisplayHeight(osd->dpy, osd->screen);
126 DBG("Screen: %dx%d depth %d\n", osd->screen_width, osd->screen_height, osd->depth);
128 int event_basep, error_basep;
129 if (!XShapeQueryExtension(osd->dpy, &event_basep, &error_basep))
130 die("XShape extension not supported by X server, giving up");
132 osd->max_lines = 2; // FIXME
133 osd->lines = xmalloc(sizeof(struct osd_line) * osd->max_lines);
138 void osd_free(struct osd_state *osd)
143 XftFontClose(osd->dpy, osd->font);
149 void osd_set_font(struct osd_state *osd, char *font_name)
152 XftFontClose(osd->dpy, osd->font);
154 DBG("Using font %s", font_name);
155 osd->font = XftFontOpenName(osd->dpy, osd->screen, font_name);
157 die("Cannot open font %s", font_name);
158 DBG("Font: asc=%d desc=%d ht=%d", osd->font->ascent, osd->font->descent, osd->font->height);
160 osd->line_distance = osd->font->height;
161 osd->line_height = osd->font->ascent;
164 struct osd_line *osd_add_line(struct osd_state *osd, enum osd_line_type type)
166 if (osd->num_lines >= osd->max_lines)
168 osd->max_lines = 2 * osd->max_lines;
169 osd->lines = xrealloc(osd->lines, sizeof(struct osd_line) * osd->max_lines);
172 struct osd_line *l = &osd->lines[osd->num_lines++];
174 l->fg_color = "green";
175 l->outline_color = "yellow";
176 l->outline_width = 0;
177 // FIXME: Colors, alignment etc.
185 die("osd_add_line: unknown type %d", type);
191 static void osd_prepare_line(struct osd_state *osd, int i)
193 struct osd_line *line = &osd->lines[i];
199 XftTextExtentsUtf8(osd->dpy, osd->font, (unsigned char *) line->u.text, strlen(line->u.text), &gi);
200 DBG("Line #%d: Glyph info: (%d,%d)+(%d,%d) off (%d,%d)\n", i, gi.x, gi.y, gi.width, gi.height, gi.xOff, gi.yOff);
201 line->width = gi.width + 2*line->outline_width;
202 line->height = osd->line_distance + 2*line->outline_width;
206 die("osd_recalc_line: unknown type %d", line->type);
209 DBG("Line #%d: Width %d (outline %d)\n", i, line->width, line->outline_width);
212 static void osd_justify_line(struct osd_state *osd, int i)
214 // FIXME: Support more modes of justification
215 struct osd_line *line = &osd->lines[i];
216 line->x_pos = (osd->win_width - line->width) / 2;
217 DBG("Line #%d: Position (%d,%d)\n", i, line->x_pos, line->y_pos);
220 static void osd_prepare(struct osd_state *osd)
224 for (int i=0; i < osd->num_lines; i++)
226 struct osd_line *line = &osd->lines[i];
227 osd_prepare_line(osd, i);
228 if (line->width > osd->win_width)
229 osd->win_width = line->width;
230 osd->win_height += line->height;
233 // FIXME: Check clipping
234 if (osd->win_width > osd->screen_width)
235 osd->win_width = osd->screen_width;
236 if (osd->win_height > osd->screen_height)
237 osd->win_height = osd->screen_height;
238 DBG("Window size set to %dx%d\n", osd->win_width, osd->win_height);
241 for (int i=0; i < osd->num_lines; i++)
243 struct osd_line *line = &osd->lines[i];
245 osd_justify_line(osd, i);
250 static void osd_draw_line(struct osd_state *osd, int i)
252 struct osd_line *line = &osd->lines[i];
255 XftColor fg_color, outline_color, mask_color;
256 XRenderColor mask_rc = { .red = 0xffff, .green = 0xffff, .blue = 0xffff, .alpha = 0xffff };
257 if (!XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->fg_color, &fg_color) ||
258 !XftColorAllocName(osd->dpy, osd->visual, osd->cmap, line->outline_color, &outline_color) ||
259 !XftColorAllocValue(osd->dpy, osd->visual, osd->cmap, &mask_rc, &mask_color))
260 die("Cannot allocate colors");
262 // Draw background in outline color
263 XftDrawRect(osd->image_draw, &outline_color, 0, line->y_pos, osd->win_width, line->height);
269 unsigned char *text = (unsigned char *) line->u.text;
270 int text_len = strlen(line->u.text);
271 XftDrawStringUtf8(osd->image_draw, &fg_color, osd->font, line->x_pos + line->outline_width, line->y_pos + line->outline_width, text, text_len);
273 // This is slow, but unlike the method used by libxosd, the result isn't ugly.
274 int outline = line->outline_width;
275 for (int dx = -outline; dx <= outline; dx++)
276 for (int dy = -outline; dy <= outline; dy++)
277 if (dx*dx + dy*dy <= outline*outline)
278 XftDrawStringUtf8(osd->mask_draw, &mask_color, osd->font, 100 + dx, 100 + dy, text, text_len);
283 die("osd_draw_line: unknown type %d", line->type);
286 XftColorFree(osd->dpy, osd->visual, osd->cmap, &fg_color);
287 XftColorFree(osd->dpy, osd->visual, osd->cmap, &outline_color);
288 XftColorFree(osd->dpy, osd->visual, osd->cmap, &mask_color);
291 void osd_show(struct osd_state *osd)
297 XSetWindowAttributes win_attr = {
298 .override_redirect = 1,
300 osd->win = XCreateWindow(osd->dpy,
303 osd->win_width, osd->win_height,
310 XStoreName(osd->dpy, osd->win, "OSD");
313 // Create image pixmap and its graphic context
314 // osd->gc can be used for both osd->win and osd->image_bitmap as they have the same root and depth
315 osd->image_pixmap = XCreatePixmap(osd->dpy, osd->win, osd->win_width, osd->win_height, osd->depth);
317 .graphics_exposures = 0,
319 osd->gc = XCreateGC(osd->dpy, osd->win, GCGraphicsExposures, &gcv);
321 // Create mask bitmap and its GC
322 osd->mask_bitmap = XCreatePixmap(osd->dpy, osd->win, osd->win_width, osd->win_height, 1);
323 osd->mask_gc = XCreateGC(osd->dpy, osd->mask_bitmap, GCGraphicsExposures, &gcv);
325 // Clear the mask bitmap
326 XSetBackground(osd->dpy, osd->mask_gc, WhitePixel(osd->dpy, osd->screen));
327 XSetForeground(osd->dpy, osd->mask_gc, BlackPixel(osd->dpy, osd->screen));
328 XFillRectangle(osd->dpy, osd->mask_bitmap, osd->mask_gc, 0, 0, osd->win_width, osd->win_height);
330 // Create XftDraw for mask and image
331 osd->mask_draw = XftDrawCreateBitmap(osd->dpy, osd->mask_bitmap);
332 osd->image_draw = XftDrawCreate(osd->dpy, osd->image_pixmap, osd->visual, osd->cmap);
333 if (!osd->mask_draw || !osd->image_draw)
334 die("Cannot create XftDraw");
336 // Draw individial lines
337 for (int i=0; i < osd->num_lines; i++)
338 osd_draw_line(osd, i);
340 XShapeCombineMask(osd->dpy, osd->win, ShapeBounding, 0, 0, osd->mask_bitmap, ShapeSet);
342 XSelectInput(osd->dpy, osd->win, ExposureMask);
343 XMapRaised(osd->dpy, osd->win);
349 void osd_hide(struct osd_state *osd)
354 XftDrawDestroy(osd->image_draw);
355 XftDrawDestroy(osd->mask_draw);
356 XFreeGC(osd->dpy, osd->gc);
357 XFreeGC(osd->dpy, osd->mask_gc);
358 XFreePixmap(osd->dpy, osd->image_pixmap);
359 XFreePixmap(osd->dpy, osd->mask_bitmap);
360 XDestroyWindow(osd->dpy, osd->win);
365 bool osd_handle_event(struct osd_state *osd, XEvent *ev)
370 if (ev->type == Expose)
372 XExposeEvent *ex = &ev->xexpose;
373 if (ex->window == osd->win)
375 DBG("Expose cnt=%d (%d,%d)+(%d,%d)\n", ex->count, ex->x, ex->y, ex->width, ex->height);
376 XCopyArea(osd->dpy, osd->image_pixmap, osd->win, osd->gc, ex->x, ex->y, ex->width, ex->height, ex->x, ex->y);