swim.git
draw.c
/* swim - window manager
* Copyright (C) 2022-2023 ArcNyxx
* see LICENCE file for licensing information */
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <ft2build.h>
#include FT_ADVANCES_H
#include FT_FREETYPE_H
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <xcb/xcb_renderutil.h>
#include "draw.h"
#include "swim.h"
#include "util.h"
#define RGB XRenderFindStandardFormat(dpy, PictStandardARGB32)
typedef struct ch {
wchar_t ch;
uint16_t adv; /* only supports left-to-right fonts */
} char_t;
typedef struct row {
char_t *data;
size_t len, alc;
} row_t;
static const struct { const char *name; uint16_t size; } fonts[1] =
{ { "/usr/share/fonts/gnu-free/FreeMono.otf", 16 } };
static const struct { uint8_t r, g, b; } clrs[CLR_LST] = { { 0x44, 0x44,
0x44 }, { 0x8C, 0x24, 0xC9 }, { 0xEE, 0xEE, 0xEE } };
static FT_Face faces[LENGTH(fonts)];
static GlyphSet glyphs;
static wchar_t array[256];
static int tlen;
static uint32_t fil;
XColor xclrs[CLR_LST];
static char_t *loadchar(wchar_t ch);
static char utf8decodebyte(const char ch, int *ext);
static int utf8decode(const char *restrict str, wchar_t *restrict val);
static char_t *
loadchar(wchar_t ch)
{
static row_t hash[128];
row_t *row = &hash[ch % LENGTH(hash)];
for (size_t i = 0; i < row->len; ++i)
if (row->data[i].ch == ch)
return &row->data[i];
FT_GlyphSlot slot = NULL;
for (size_t i = 0; i < LENGTH(fonts); ++i) {
uint32_t code;
if ((code = FT_Get_Char_Index(faces[i], ch)) == 0 ||
FT_Load_Glyph(faces[i], code, FT_LOAD_RENDER))
continue;
slot = faces[i]->glyph;
break;
}
if (slot == NULL) {
char_t *data;
if ((data = loadchar(L' ')) != NULL)
return data;
die("swim: unable to load char\n");
}
XGlyphInfo gi = {
.x = -slot->bitmap_left, .y = slot->bitmap_top,
.width = slot->bitmap.width, .height = slot->bitmap.rows,
.xOff = slot->advance.x >> 6, .yOff = slot->advance.y >> 6
};
char *img = srealloc(NULL, 4 * gi.width * gi.height);
for (size_t y = 0; y < gi.height; ++y)
for (size_t x = 0; x < gi.width; ++x)
for (size_t i = 0; i < 4; ++i)
img[4 * (y * gi.width + x) + i] = slot->bitmap.
buffer[y * gi.width + x];
XRenderAddGlyphs(dpy, glyphs, (void *)&ch, &gi, 1, img,
4 * gi.width * gi.height);
free(img);
if (row->alc == row->len)
row->data = srealloc(row->data, (row->alc = 2 * row->alc + 1) *
sizeof(char_t));
row->data[row->len] = (char_t){ ch, slot->advance.x >> 6 };
return &row->data[row->len++];
}
static char
utf8decodebyte(const char ch, int *ext)
{
static const unsigned char byte[5] = { 0x80, 0, 0xC0, 0xE0, 0xF0 };
static const unsigned char mask[5] = { 0xC0, 0x80, 0xE0, 0xF0, 0xF8 };
for (*ext = 0; *ext < 5; ++(*ext))
if ((ch & mask[*ext]) == byte[*ext])
return ch & ~mask[*ext];
*ext = -1;
return 0;
}
static int
utf8decode(const char *restrict str, wchar_t *restrict val)
{
static const int min[5] = { 0, 0, 0x80, 0x800, 0x10000 };
static const int max[5] = { 0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF };
int len; *val = 0xFFFD;
wchar_t decoded = utf8decodebyte(str[0], &len);
for (int i = 1, type; i < len; ++i) {
decoded = (decoded << 6) + utf8decodebyte(str[i], &type);
if (type != 0)
return i;
}
if (len >= 1 && decoded <= max[len] && decoded >= min[len] &&
(decoded < 0xD800 || decoded > 0xDFFF))
*val = decoded;
return MAX(1, len);
}
extern xcb_connection_t *c;
extern xcb_screen_t *screen;
xcb_render_query_pict_formats_reply_t *pfr;
uint32_t visual_fmt, idt;
void
drawinit(void)
{
static FT_Library lib;
if (FT_Init_FreeType(&lib))
die("swim: unable to init freetype\n");
for (size_t i = 0; i < LENGTH(fonts); ++i)
if (FT_New_Face(lib, fonts[i].name, 0, &faces[i]) ||
FT_Set_Char_Size(faces[i],
fonts[i].size << 6, 0, 0, 0))
die("swim: unable to load face: %s\n", fonts[i].name);
pfr = xcb_render_query_pict_formats_reply(c,
xcb_render_query_pict_formats_unchecked(c), NULL);
xcb_pixmap_t pix = ID(c); fil = ID(c);
xcb_create_pixmap(c, 32, pix, screen->root, 1, 1);
xcb_render_pictvisual_t *vft = xcb_render_util_find_visual_format(pfr,
screen->root_visual);
visual_fmt = vft->format;
xcb_render_pictforminfo_t *std = xcb_render_util_find_standard_format(
pfr, XCB_PICT_STANDARD_ARGB_32);
xcb_render_create_picture(c, fil, pix, std->id, 0, NULL);
idt = std->id;
xcb_free_pixmap(c, pix); /* storage freed when references destroyed */
glyphs = XRenderCreateGlyphSet(dpy, RGB);
for (size_t i = 0; i < LENGTH(clrs); ++i) {
xclrs[i] = (XColor){
.red = (clrs[i].r << 8) + clrs[i].r,
.green = (clrs[i].g << 8) + clrs[i].g,
.blue = (clrs[i].b << 8) + clrs[i].b,
.flags = DoRed | DoGreen | DoBlue
};
XAllocColor(dpy, DEF(Colormap), &xclrs[i]);
}
}
void
drawwide(const char *restrict str, uint16_t max, uint16_t *restrict width)
{
tlen = 0;
for (int i = 0; i < 256 && str[i] != '\0'; ++tlen) {
i += utf8decode(&str[i], &array[tlen]);
char_t *data = loadchar(array[tlen]);
if ((*width += data->adv) > max) {
*width = max; break;
}
}
}
void
drawrect(const mon_t *mon, clr_t col, bool fill,
uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
XSetForeground(dpy, DEF(GC), xclrs[col].pixel);
(fill ? XFillRectangle : XDrawRectangle)(dpy, mon->win, DEF(GC), x, y,
w - !fill, h - !fill);
}
void
drawtext(const mon_t *mon, clr_t bg, clr_t fg, uint16_t x, uint16_t w)
{
drawrect(mon, bg, true, x, 0, w, H);
XRenderFillRectangle(dpy, PictOpSrc, fil, &(XRenderColor){
(clrs[fg].r << 8) + clrs[fg].r, (clrs[fg].g << 8) + clrs[fg].g,
(clrs[fg].b << 8) + clrs[fg].b, 0xFFFF
}, 0, 0, 1, 1);
XRenderCompositeString32(dpy, PictOpOver, fil, mon->pic, RGB,
glyphs, 0, 0, x + PW / 2, H - 4, (void *)array, tlen);
}