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 }