diff options
Diffstat (limited to 'mu_x11.c')
| -rw-r--r-- | mu_x11.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/mu_x11.c b/mu_x11.c new file mode 100644 index 0000000..13b23c2 --- /dev/null +++ b/mu_x11.c @@ -0,0 +1,148 @@ +/* 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 <X11/Xlib.h> +#include <X11/Xft/Xft.h> +#include <stdio.h> +#include <string.h> +#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); +} |
