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 }