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 (10719B)


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