/* mu_x11.c -- an Xlib/Xft backend for microui. * * microui is renderer-agnostic: it emits a command list of clipped * rectangles, text runs and icons, and asks the host two questions about * font metrics. This answers those and draws the commands into an * off-screen pixmap, then blits it, so a frame never appears half-drawn. * * SPDX-License-Identifier: MIT (matching the vendored microui) */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include "vendor/microui.h" #include "mu_x11.h" static Display *dpy; static Window win; static Visual *vis; static Colormap cmap; static XftFont *fnt; static XftDraw *xd; static Pixmap buf; static GC gc; static int bw, bh; int mux_text_width(mu_Font font, const char *s, int len) { (void)font; XGlyphInfo gi; if (len < 0) len = strlen(s); XftTextExtentsUtf8(dpy, fnt, (const FcChar8 *)s, len, &gi); return gi.xOff; } int mux_text_height(mu_Font font) { (void)font; return fnt->ascent + fnt->descent; } void mux_init(Display *d, Window w, const char *fontname) { dpy = d; win = w; int scr = DefaultScreen(dpy); vis = DefaultVisual(dpy, scr); cmap = DefaultColormap(dpy, scr); fnt = XftFontOpenName(dpy, scr, fontname); gc = XCreateGC(dpy, win, 0, NULL); buf = 0; xd = NULL; bw = bh = 0; } static void ensure_buffer(int w, int h) { if (buf && w == bw && h == bh) return; if (xd) { XftDrawDestroy(xd); xd = NULL; } if (buf) { XFreePixmap(dpy, buf); buf = 0; } buf = XCreatePixmap(dpy, win, w, h, DefaultDepth(dpy, DefaultScreen(dpy))); xd = XftDrawCreate(dpy, buf, vis, cmap); bw = w; bh = h; } static void xft_of(mu_Color c, XftColor *out) { XRenderColor rc = { c.r * 257, c.g * 257, c.b * 257, c.a * 257 }; XftColorAllocValue(dpy, vis, cmap, &rc, out); } static const char *icon_glyph(int id) { switch (id) { case MU_ICON_CLOSE: return "x"; case MU_ICON_CHECK: return "*"; case MU_ICON_COLLAPSED: return ">"; case MU_ICON_EXPANDED: return "v"; default: return "?"; } } /* CLAMP BEFORE CONVERTING. XRectangle's w/h are unsigned SHORT, and microui resets its scissor by emitting an "unclipped" rect of 0x1000000 -- which truncates to 0 in 16 bits, turning "clip nothing" into "clip everything". The symptom is that a panel draws its first screenful and the rest silently vanishes, which reads as a layout or buffer bug and is neither. */ static void set_clip(mu_Rect r) { int x = r.x, y = r.y, w = r.w, h = r.h; if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (w > bw - x) w = bw - x; if (h > bh - y) h = bh - y; if (w <= 0 || h <= 0) { w = 0; h = 0; } XRectangle xr = { (short)x, (short)y, (unsigned short)w, (unsigned short)h }; Region reg = XCreateRegion(); XUnionRectWithRegion(&xr, reg, reg); XftDrawSetClip(xd, reg); XDestroyRegion(reg); } void mux_present(mu_Context *ctx, int w, int h, mu_Color bg) { ensure_buffer(w, h); XftDrawSetClip(xd, NULL); XftColor col; xft_of(bg, &col); XftDrawRect(xd, &col, 0, 0, w, h); XftColorFree(dpy, vis, cmap, &col); mu_Command *cmd = NULL; while (mu_next_command(ctx, &cmd)) { switch (cmd->type) { case MU_COMMAND_RECT: xft_of(cmd->rect.color, &col); XftDrawRect(xd, &col, cmd->rect.rect.x, cmd->rect.rect.y, cmd->rect.rect.w, cmd->rect.rect.h); XftColorFree(dpy, vis, cmap, &col); break; case MU_COMMAND_TEXT: xft_of(cmd->text.color, &col); XftDrawStringUtf8(xd, &col, fnt, cmd->text.pos.x, cmd->text.pos.y + fnt->ascent, (const FcChar8 *)cmd->text.str, strlen(cmd->text.str)); XftColorFree(dpy, vis, cmap, &col); break; case MU_COMMAND_ICON: { const char *g = icon_glyph(cmd->icon.id); int tw = mux_text_width(NULL, g, 1); xft_of(cmd->icon.color, &col); XftDrawStringUtf8(xd, &col, fnt, cmd->icon.rect.x + (cmd->icon.rect.w - tw) / 2, cmd->icon.rect.y + fnt->ascent + (cmd->icon.rect.h - mux_text_height(NULL)) / 2, (const FcChar8 *)g, 1); XftColorFree(dpy, vis, cmap, &col); break; } case MU_COMMAND_CLIP: set_clip(cmd->clip.rect); break; } } XftDrawSetClip(xd, NULL); XCopyArea(dpy, buf, win, gc, 0, 0, w, h, 0, 0); XFlush(dpy); }