tmenu

Unnamed repository; edit this file 'description' to name the repository.
git clone https://git.inz.fi/tmenu
Log | Files | Refs | README | LICENSE

drw.c (9637B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <limits.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <strings.h>
      8 #include <term.h>
      9 #include <wchar.h>
     10 
     11 #include "drw.h"
     12 #include "util.h"
     13 
     14 #define UTF_INVALID 0xFFFD
     15 
     16 static FILE *ttyout;
     17 static char *cap_cup;
     18 static char *cap_sgr0;
     19 static char *cap_setaf;
     20 static char *cap_setab;
     21 static char *cap_sc;
     22 static char *cap_rc;
     23 static int termcolors = 8;
     24 
     25 static int
     26 ttyputc(int c)
     27 {
     28 	return ttyout ? fputc(c, ttyout) : EOF;
     29 }
     30 
     31 static void
     32 putcap(char *cap)
     33 {
     34 	if (cap)
     35 		tputs(cap, 1, ttyputc);
     36 }
     37 
     38 static void
     39 setcolor(short fg, short bg)
     40 {
     41 	if (cap_setaf && fg >= 0)
     42 		putcap(tparm(cap_setaf, fg));
     43 	if (cap_setab && bg >= 0)
     44 		putcap(tparm(cap_setab, bg));
     45 }
     46 
     47 static int
     48 utf8decode(const char *s_in, long *u, int *err)
     49 {
     50 	static const unsigned char lens[] = {
     51 		/* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
     52 		/* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0,  /* invalid */
     53 		/* 110XX */ 2, 2, 2, 2,
     54 		/* 1110X */ 3, 3,
     55 		/* 11110 */ 4,
     56 		/* 11111 */ 0,  /* invalid */
     57 	};
     58 	static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
     59 	static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
     60 	const unsigned char *s = (const unsigned char *)s_in;
     61 	int len = lens[*s >> 3], i;
     62 	long cp;
     63 
     64 	*u = UTF_INVALID;
     65 	*err = 1;
     66 	if (len == 0)
     67 		return 1;
     68 
     69 	cp = s[0] & leading_mask[len - 1];
     70 	for (i = 1; i < len; ++i) {
     71 		if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
     72 			return i;
     73 		cp = (cp << 6) | (s[i] & 0x3F);
     74 	}
     75 	/* out of range, surrogate, overlong encoding */
     76 	if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
     77 		return len;
     78 
     79 	*err = 0;
     80 	*u = cp;
     81 	return len;
     82 }
     83 
     84 static short
     85 rgb2color(int r, int g, int b)
     86 {
     87 	static const struct { short id; int r, g, b; } vga[] = {
     88 		{ 0, 0x00, 0x00, 0x00 },
     89 		{ 1, 0x80, 0x00, 0x00 },
     90 		{ 2, 0x00, 0x80, 0x00 },
     91 		{ 3, 0x80, 0x80, 0x00 },
     92 		{ 4, 0x00, 0x00, 0x80 },
     93 		{ 5, 0x80, 0x00, 0x80 },
     94 		{ 6, 0x00, 0x80, 0x80 },
     95 		{ 7, 0xc0, 0xc0, 0xc0 },
     96 		{ 8, 0x80, 0x80, 0x80 },
     97 		{ 9, 0xff, 0x00, 0x00 },
     98 		{ 10, 0x00, 0xff, 0x00 },
     99 		{ 11, 0xff, 0xff, 0x00 },
    100 		{ 12, 0x00, 0x00, 0xff },
    101 		{ 13, 0xff, 0x00, 0xff },
    102 		{ 14, 0x00, 0xff, 0xff },
    103 		{ 15, 0xff, 0xff, 0xff },
    104 	};
    105 	static const int cube[] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
    106 	int best = 0, bestdist = INT_MAX, i, dist;
    107 
    108 	if (termcolors >= 256) {
    109 		int cr = 0, cg = 0, cb = 0, gray, grayv, idx;
    110 
    111 		for (i = 1; i < LENGTH(cube); i++) {
    112 			if (abs(r - cube[i]) < abs(r - cube[cr]))
    113 				cr = i;
    114 			if (abs(g - cube[i]) < abs(g - cube[cg]))
    115 				cg = i;
    116 			if (abs(b - cube[i]) < abs(b - cube[cb]))
    117 				cb = i;
    118 		}
    119 
    120 		for (i = 0; i < LENGTH(vga); i++) {
    121 			dist = (r - vga[i].r) * (r - vga[i].r)
    122 			     + (g - vga[i].g) * (g - vga[i].g)
    123 			     + (b - vga[i].b) * (b - vga[i].b);
    124 			if (dist < bestdist) {
    125 				best = vga[i].id;
    126 				bestdist = dist;
    127 			}
    128 		}
    129 
    130 		idx = 16 + 36 * cr + 6 * cg + cb;
    131 		dist = (r - cube[cr]) * (r - cube[cr])
    132 		     + (g - cube[cg]) * (g - cube[cg])
    133 		     + (b - cube[cb]) * (b - cube[cb]);
    134 		if (dist < bestdist)
    135 			best = idx, bestdist = dist;
    136 
    137 		gray = (r + g + b) / 3;
    138 		gray = (gray - 8 + 5) / 10;
    139 		if (gray < 0)
    140 			gray = 0;
    141 		if (gray > 23)
    142 			gray = 23;
    143 		idx = 232 + gray;
    144 		grayv = 8 + gray * 10;
    145 		dist = (r - grayv) * (r - grayv)
    146 		     + (g - grayv) * (g - grayv)
    147 		     + (b - grayv) * (b - grayv);
    148 		if (dist < bestdist)
    149 			best = idx;
    150 		return best;
    151 	}
    152 
    153 	for (i = 0; i < 8; i++) {
    154 		dist = (r - vga[i].r) * (r - vga[i].r)
    155 		     + (g - vga[i].g) * (g - vga[i].g)
    156 		     + (b - vga[i].b) * (b - vga[i].b);
    157 		if (dist < bestdist) {
    158 			best = vga[i].id;
    159 			bestdist = dist;
    160 		}
    161 	}
    162 	return best;
    163 }
    164 
    165 static short
    166 parsecolor(const char *clrname)
    167 {
    168 	unsigned int r, g, b;
    169 
    170 	if (!clrname || !*clrname)
    171 		return -1;
    172 	if (clrname[0] == '#' && strlen(clrname) == 7
    173 	&& sscanf(clrname + 1, "%02x%02x%02x", &r, &g, &b) == 3)
    174 		return rgb2color(r, g, b);
    175 	if (!strcasecmp(clrname, "black"))
    176 		return 0;
    177 	if (!strcasecmp(clrname, "red"))
    178 		return 1;
    179 	if (!strcasecmp(clrname, "green"))
    180 		return 2;
    181 	if (!strcasecmp(clrname, "yellow"))
    182 		return 3;
    183 	if (!strcasecmp(clrname, "blue"))
    184 		return 4;
    185 	if (!strcasecmp(clrname, "magenta"))
    186 		return 5;
    187 	if (!strcasecmp(clrname, "cyan"))
    188 		return 6;
    189 	if (!strcasecmp(clrname, "white"))
    190 		return 7;
    191 	return -1;
    192 }
    193 
    194 static int
    195 textwidth(const char *text, unsigned int *bytes, unsigned int limit)
    196 {
    197 	int width = 0, err, len, rw;
    198 	long cp;
    199 	unsigned int used = 0;
    200 
    201 	if (!text)
    202 		return 0;
    203 	while (*text) {
    204 		len = utf8decode(text, &cp, &err);
    205 		rw = err ? 1 : wcwidth((wchar_t)cp);
    206 		if (rw < 0)
    207 			rw = 1;
    208 		if (limit && width + rw > (int)limit)
    209 			break;
    210 		width += rw;
    211 		used += len;
    212 		text += len;
    213 	}
    214 	if (bytes)
    215 		*bytes = used;
    216 	return width;
    217 }
    218 
    219 static void
    220 move_cursor(int x, int y)
    221 {
    222 	if (!cap_cup)
    223 		return;
    224 	putcap(tparm(cap_cup, y, x));
    225 }
    226 
    227 static void
    228 fill_spaces(unsigned int n)
    229 {
    230 	while (n-- > 0)
    231 		ttyputc(' ');
    232 }
    233 
    234 Drw *
    235 drw_create(void *unused1, int unused2, unsigned long unused3, unsigned int w, unsigned int h)
    236 {
    237 	Drw *drw = ecalloc(1, sizeof(Drw));
    238 
    239 	(void)unused1;
    240 	(void)unused2;
    241 	(void)unused3;
    242 	drw->w = w;
    243 	drw->h = h;
    244 	return drw;
    245 }
    246 
    247 void
    248 drw_resize(Drw *drw, unsigned int w, unsigned int h)
    249 {
    250 	if (!drw)
    251 		return;
    252 	drw->w = w;
    253 	drw->h = h;
    254 }
    255 
    256 void
    257 drw_free(Drw *drw)
    258 {
    259 	if (!drw)
    260 		return;
    261 	drw_fontset_free(drw->fonts);
    262 	free(drw);
    263 }
    264 
    265 Fnt *
    266 drw_fontset_create(Drw *drw, const char *fonts[], size_t fontcount)
    267 {
    268 	Fnt *font;
    269 
    270 	if (!drw || !fontcount || !fonts)
    271 		return NULL;
    272 	font = ecalloc(1, sizeof(Fnt));
    273 	font->h = 1;
    274 	return drw->fonts = font;
    275 }
    276 
    277 void
    278 drw_fontset_free(Fnt *font)
    279 {
    280 	free(font);
    281 }
    282 
    283 void
    284 drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
    285 {
    286 	(void)drw;
    287 	dest->fg = parsecolor(clrname);
    288 	dest->bg = -1;
    289 	dest->pair = 0;
    290 }
    291 
    292 Clr *
    293 drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
    294 {
    295 	Clr *ret;
    296 	size_t i;
    297 
    298 	if (!drw || !clrnames || clrcount < 2)
    299 		return NULL;
    300 	ret = ecalloc(clrcount, sizeof(Clr));
    301 	for (i = 0; i < clrcount; i++)
    302 		drw_clr_create(drw, &ret[i], clrnames[i]);
    303 	ret[ColBg].bg = ret[ColBg].fg;
    304 	ret[ColFg].pair = 0;
    305 	ret[ColBg].pair = 0;
    306 	return ret;
    307 }
    308 
    309 void
    310 drw_clr_free(Drw *drw, Clr *c)
    311 {
    312 	(void)drw;
    313 	(void)c;
    314 }
    315 
    316 void
    317 drw_scm_free(Drw *drw, Clr *scm, size_t clrcount)
    318 {
    319 	size_t i;
    320 
    321 	if (!drw || !scm)
    322 		return;
    323 	for (i = 0; i < clrcount; i++)
    324 		drw_clr_free(drw, &scm[i]);
    325 	free(scm);
    326 }
    327 
    328 void
    329 drw_setfontset(Drw *drw, Fnt *set)
    330 {
    331 	if (drw)
    332 		drw->fonts = set;
    333 }
    334 
    335 void
    336 drw_setscheme(Drw *drw, Clr *scm)
    337 {
    338 	if (drw)
    339 		drw->scheme = scm;
    340 }
    341 
    342 void
    343 drw_term_init(FILE *out)
    344 {
    345 	ttyout = out;
    346 	termcolors = tigetnum("colors");
    347 	cap_cup = tigetstr("cup");
    348 	cap_sgr0 = tigetstr("sgr0");
    349 	cap_setaf = tigetstr("setaf");
    350 	cap_setab = tigetstr("setab");
    351 	cap_sc = tigetstr("sc");
    352 	cap_rc = tigetstr("rc");
    353 	if (termcolors < 0)
    354 		termcolors = 8;
    355 	if (cap_cup == (char *)-1)
    356 		cap_cup = NULL;
    357 	if (cap_sgr0 == (char *)-1)
    358 		cap_sgr0 = NULL;
    359 	if (cap_setaf == (char *)-1)
    360 		cap_setaf = NULL;
    361 	if (cap_setab == (char *)-1)
    362 		cap_setab = NULL;
    363 	if (cap_sc == (char *)-1)
    364 		cap_sc = NULL;
    365 	if (cap_rc == (char *)-1)
    366 		cap_rc = NULL;
    367 }
    368 
    369 void
    370 drw_term_reset(void)
    371 {
    372 	putcap(cap_sgr0);
    373 	if (ttyout)
    374 		fflush(ttyout);
    375 }
    376 
    377 void
    378 drw_cursor_save(void)
    379 {
    380 	if (cap_sc)
    381 		putcap(cap_sc);
    382 	else if (ttyout)
    383 		fputs("\0337", ttyout);
    384 }
    385 
    386 void
    387 drw_cursor_restore(void)
    388 {
    389 	if (cap_rc)
    390 		putcap(cap_rc);
    391 	else if (ttyout)
    392 		fputs("\0338", ttyout);
    393 }
    394 
    395 void
    396 drw_move(Drw *drw, int x, int y)
    397 {
    398 	(void)drw;
    399 	move_cursor(x, y);
    400 }
    401 
    402 void
    403 drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
    404 {
    405 	unsigned int i;
    406 	short fg, bg;
    407 
    408 	if (!drw || !drw->scheme || !filled)
    409 		return;
    410 	fg = invert ? drw->scheme[ColBg].fg : drw->scheme[ColFg].fg;
    411 	bg = invert ? drw->scheme[ColFg].fg : drw->scheme[ColBg].fg;
    412 	setcolor(fg, bg);
    413 	for (i = 0; i < h; i++) {
    414 		move_cursor(x, y + (int)i);
    415 		fill_spaces(w);
    416 	}
    417 }
    418 
    419 int
    420 drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
    421 {
    422 	unsigned int i, bytes = 0, avail;
    423 	int width;
    424 	short fg, bg;
    425 
    426 	if (!drw || !text)
    427 		return x;
    428 	if (!w)
    429 		return textwidth(text, NULL, invert ? (unsigned int)invert : 0);
    430 
    431 	fg = invert ? drw->scheme[ColBg].fg : drw->scheme[ColFg].fg;
    432 	bg = invert ? drw->scheme[ColFg].fg : drw->scheme[ColBg].fg;
    433 	setcolor(fg, bg);
    434 	for (i = 0; i < h; i++) {
    435 		move_cursor(x, y + (int)i);
    436 		fill_spaces(w);
    437 	}
    438 	if (w < lpad)
    439 		return x + w;
    440 
    441 	x += lpad;
    442 	avail = w - lpad;
    443 	width = textwidth(text, &bytes, avail);
    444 	if (bytes) {
    445 		move_cursor(x, y + (int)h / 2);
    446 		fwrite(text, 1, bytes, ttyout);
    447 	}
    448 	return x + width;
    449 }
    450 
    451 void
    452 drw_map(Drw *drw, unsigned long win, int x, int y, unsigned int w, unsigned int h)
    453 {
    454 	(void)drw;
    455 	(void)win;
    456 	(void)x;
    457 	(void)y;
    458 	(void)w;
    459 	(void)h;
    460 	putcap(cap_sgr0);
    461 	if (ttyout)
    462 		fflush(ttyout);
    463 }
    464 
    465 unsigned int
    466 drw_fontset_getwidth(Drw *drw, const char *text)
    467 {
    468 	if (!drw || !drw->fonts || !text)
    469 		return 0;
    470 	return textwidth(text, NULL, 0);
    471 }
    472 
    473 unsigned int
    474 drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
    475 {
    476 	if (!drw || !drw->fonts || !text)
    477 		return 0;
    478 	return textwidth(text, NULL, n);
    479 }
    480 
    481 void
    482 drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
    483 {
    484 	char *buf;
    485 
    486 	if (!font || !text)
    487 		return;
    488 	buf = ecalloc(len + 1, sizeof(char));
    489 	memcpy(buf, text, len);
    490 	if (w)
    491 		*w = textwidth(buf, NULL, 0);
    492 	if (h)
    493 		*h = font->h;
    494 	free(buf);
    495 }
    496 
    497 Cur *
    498 drw_cur_create(Drw *drw, int shape)
    499 {
    500 	Cur *cur;
    501 
    502 	(void)drw;
    503 	cur = ecalloc(1, sizeof(Cur));
    504 	cur->cursor = shape;
    505 	return cur;
    506 }
    507 
    508 void
    509 drw_cur_free(Drw *drw, Cur *cursor)
    510 {
    511 	(void)drw;
    512 	free(cursor);
    513 }