t* My version of sent URI git clone git://git.codevoid.de/sent-sdk.git DIR Log DIR Files DIR Refs DIR README DIR LICENSE --- tsent.c (17491B) --- 1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/types.h> 3 #include <arpa/inet.h> 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <math.h> 8 #include <regex.h> 9 #include <stdarg.h> 10 #include <stdio.h> 11 #include <stdint.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <X11/keysym.h> 16 #include <X11/XKBlib.h> 17 #include <X11/Xatom.h> 18 #include <X11/Xlib.h> 19 #include <X11/Xutil.h> 20 #include <X11/Xft/Xft.h> 21 22 #include "arg.h" 23 #include "util.h" 24 #include "drw.h" 25 26 char *argv0; 27 28 /* macros */ 29 #define LEN(a) (sizeof(a) / sizeof(a)[0]) 30 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 31 #define MAXFONTSTRLEN 128 32 33 typedef enum { 34 NONE = 0, 35 SCALED = 1, 36 } imgstate; 37 38 typedef struct { 39 unsigned char *buf; 40 unsigned int bufwidth, bufheight; 41 imgstate state; 42 XImage *ximg; 43 int numpasses; 44 } Image; 45 46 typedef struct { 47 char *regex; 48 char *bin; 49 } Filter; 50 51 typedef struct { 52 unsigned int linecount; 53 char **lines; 54 Image *img; 55 char *embed; 56 } Slide; 57 58 /* Purely graphic info */ 59 typedef struct { 60 Display *dpy; 61 Window win; 62 Atom wmdeletewin, netwmname; 63 Visual *vis; 64 XSetWindowAttributes attrs; 65 int scr; 66 int w, h; 67 int uw, uh; /* usable dimensions for drawing text and images */ 68 } XWindow; 69 70 typedef union { 71 int i; 72 unsigned int ui; 73 float f; 74 const void *v; 75 } Arg; 76 77 typedef struct { 78 unsigned int b; 79 void (*func)(const Arg *); 80 const Arg arg; 81 } Mousekey; 82 83 typedef struct { 84 KeySym keysym; 85 void (*func)(const Arg *); 86 const Arg arg; 87 } Shortcut; 88 89 static void fffree(Image *img); 90 static void ffload(Slide *s); 91 static void ffprepare(Image *img); 92 static void ffscale(Image *img); 93 static void ffdraw(Image *img); 94 95 static void getfontsize(Slide *s, unsigned int *width, unsigned int *height); 96 static void cleanup(int slidesonly); 97 static void reload(const Arg *arg); 98 static void load(FILE *fp); 99 static void advance(const Arg *arg); 100 static void quit(const Arg *arg); 101 static void resize(int width, int height); 102 static void run(); 103 static void usage(); 104 static void xdraw(); 105 static void xhints(); 106 static void xinit(); 107 static void xloadfonts(); 108 109 static void bpress(XEvent *); 110 static void cmessage(XEvent *); 111 static void expose(XEvent *); 112 static void kpress(XEvent *); 113 static void configure(XEvent *); 114 115 /* config.h for applying patches and the configuration. */ 116 #include "config.h" 117 118 /* Globals */ 119 static const char *fname = NULL; 120 static Slide *slides = NULL; 121 static int idx = 0; 122 static int slidecount = 0; 123 static XWindow xw; 124 static Drw *d = NULL; 125 static Clr *sc; 126 static Fnt *fonts[NUMFONTSCALES]; 127 static int running = 1; 128 129 static void (*handler[LASTEvent])(XEvent *) = { 130 [ButtonPress] = bpress, 131 [ClientMessage] = cmessage, 132 [ConfigureNotify] = configure, 133 [Expose] = expose, 134 [KeyPress] = kpress, 135 }; 136 137 int 138 filter(int fd, const char *cmd) 139 { 140 int fds[2]; 141 142 if (pipe(fds) < 0) 143 die("sent: Unable to create pipe:"); 144 145 switch (fork()) { 146 case -1: 147 die("sent: Unable to fork:"); 148 case 0: 149 dup2(fd, 0); 150 dup2(fds[1], 1); 151 close(fds[0]); 152 close(fds[1]); 153 execlp("sh", "sh", "-c", cmd, (char *)0); 154 fprintf(stderr, "sent: execlp sh -c '%s': %s\n", cmd, strerror(errno)); 155 _exit(1); 156 } 157 close(fds[1]); 158 return fds[0]; 159 } 160 161 void 162 fffree(Image *img) 163 { 164 free(img->buf); 165 if (img->ximg) 166 XDestroyImage(img->ximg); 167 free(img); 168 } 169 170 void 171 ffload(Slide *s) 172 { 173 uint32_t y, x; 174 uint16_t *row; 175 uint8_t opac, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b; 176 size_t rowlen, off, nbytes, i; 177 ssize_t count; 178 unsigned char hdr[16]; 179 char *bin = NULL; 180 char *filename; 181 regex_t regex; 182 int fdin, fdout; 183 184 if (s->img || !(filename = s->embed) || !s->embed[0]) 185 return; /* already done */ 186 187 for (i = 0; i < LEN(filters); i++) { 188 if (regcomp(®ex, filters[i].regex, 189 REG_NOSUB | REG_EXTENDED | REG_ICASE)) { 190 fprintf(stderr, "sent: Invalid regex '%s'\n", filters[i].regex); 191 continue; 192 } 193 if (!regexec(®ex, filename, 0, NULL, 0)) { 194 bin = filters[i].bin; 195 regfree(®ex); 196 break; 197 } 198 regfree(®ex); 199 } 200 if (!bin) 201 die("sent: Unable to find matching filter for '%s'", filename); 202 203 if ((fdin = open(filename, O_RDONLY)) < 0) 204 die("sent: Unable to open '%s':", filename); 205 206 if ((fdout = filter(fdin, bin)) < 0) 207 die("sent: Unable to filter '%s':", filename); 208 close(fdin); 209 210 if (read(fdout, hdr, 16) != 16) 211 die("sent: Unable to read filtered file '%s':", filename); 212 if (memcmp("farbfeld", hdr, 8)) 213 die("sent: Filtered file '%s' has no valid farbfeld header", filename); 214 215 s->img = ecalloc(1, sizeof(Image)); 216 s->img->bufwidth = ntohl(*(uint32_t *)&hdr[8]); 217 s->img->bufheight = ntohl(*(uint32_t *)&hdr[12]); 218 219 if (s->img->buf) 220 free(s->img->buf); 221 /* internally the image is stored in 888 format */ 222 s->img->buf = ecalloc(s->img->bufwidth * s->img->bufheight, strlen("888")); 223 224 /* scratch buffer to read row by row */ 225 rowlen = s->img->bufwidth * 2 * strlen("RGBA"); 226 row = ecalloc(1, rowlen); 227 228 /* extract window background color channels for transparency */ 229 bg_r = (sc[ColBg].pixel >> 16) % 256; 230 bg_g = (sc[ColBg].pixel >> 8) % 256; 231 bg_b = (sc[ColBg].pixel >> 0) % 256; 232 233 for (off = 0, y = 0; y < s->img->bufheight; y++) { 234 nbytes = 0; 235 while (nbytes < rowlen) { 236 count = read(fdout, (char *)row + nbytes, rowlen - nbytes); 237 if (count < 0) 238 die("sent: Unable to read from pipe:"); 239 nbytes += count; 240 } 241 for (x = 0; x < rowlen / 2; x += 4) { 242 fg_r = ntohs(row[x + 0]) / 257; 243 fg_g = ntohs(row[x + 1]) / 257; 244 fg_b = ntohs(row[x + 2]) / 257; 245 opac = ntohs(row[x + 3]) / 257; 246 /* blend opaque part of image data with window background color to 247 * emulate transparency */ 248 s->img->buf[off++] = (fg_r * opac + bg_r * (255 - opac)) / 255; 249 s->img->buf[off++] = (fg_g * opac + bg_g * (255 - opac)) / 255; 250 s->img->buf[off++] = (fg_b * opac + bg_b * (255 - opac)) / 255; 251 } 252 } 253 254 free(row); 255 close(fdout); 256 } 257 258 void 259 ffprepare(Image *img) 260 { 261 int depth = DefaultDepth(xw.dpy, xw.scr); 262 int width = xw.uw; 263 int height = xw.uh; 264 265 if (xw.uw * img->bufheight > xw.uh * img->bufwidth) 266 width = img->bufwidth * xw.uh / img->bufheight; 267 else 268 height = img->bufheight * xw.uw / img->bufwidth; 269 270 if (depth < 24) 271 die("sent: Display color depths < 24 not supported"); 272 273 if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0, 274 NULL, width, height, 32, 0))) 275 die("sent: Unable to create XImage"); 276 277 img->ximg->data = ecalloc(height, img->ximg->bytes_per_line); 278 if (!XInitImage(img->ximg)) 279 die("sent: Unable to initiate XImage"); 280 281 ffscale(img); 282 img->state |= SCALED; 283 } 284 285 static unsigned char double_to_uchar_clamp255(double dbl) 286 { 287 dbl = round(dbl); 288 289 return 290 (dbl < 0.0) ? 0 : 291 (dbl > 255.0) ? 255 : (unsigned char)dbl; 292 } 293 294 static int int_clamp(int integer, int lower, int upper) 295 { 296 if (integer < lower) 297 return lower; 298 else if (integer >= upper) 299 return upper - 1; 300 else 301 return integer; 302 } 303 304 void 305 ffscale(Image *img) 306 { 307 const unsigned width = img->ximg->width; 308 const unsigned height = img->ximg->height; 309 unsigned char* newBuf = (unsigned char*)img->ximg->data; 310 const unsigned jdy = img->ximg->bytes_per_line / 4 - width; 311 312 const double x_scale = ((double)img->bufwidth/(double)width); 313 const double y_scale = ((double)img->bufheight/(double)height); 314 315 for (unsigned y = 0; y < height; ++y) { 316 const double old_y = (double)y * y_scale; 317 const double y_factor = ceil(old_y) - old_y; 318 const int old_y_int_0 = int_clamp((int)floor(old_y), 0, img->bufheight); 319 const int old_y_int_1 = int_clamp((int)ceil(old_y), 0, img->bufheight); 320 321 for (unsigned x = 0; x < width; ++x) { 322 const double old_x = (double)x * x_scale; 323 const double x_factor = ceil(old_x) - old_x; 324 const int old_x_int_0 = int_clamp((int)floor(old_x), 0, img->bufwidth); 325 const int old_x_int_1 = int_clamp((int)ceil(old_x), 0, img->bufwidth); 326 327 const unsigned c00_pos = 3*((old_x_int_0) + ((old_y_int_0)*img->bufwidth)); 328 const unsigned c01_pos = 3*((old_x_int_0) + ((old_y_int_1)*img->bufwidth)); 329 const unsigned c10_pos = 3*((old_x_int_1) + ((old_y_int_0)*img->bufwidth)); 330 const unsigned c11_pos = 3*((old_x_int_1) + ((old_y_int_1)*img->bufwidth)); 331 332 for (int i = 2; i >= 0 ; --i) { 333 const unsigned char c00 = img->buf[c00_pos + i]; 334 const unsigned char c01 = img->buf[c01_pos + i]; 335 const unsigned char c10 = img->buf[c10_pos + i]; 336 const unsigned char c11 = img->buf[c11_pos + i]; 337 338 const double x_result_0 = (double)c00*x_factor + (double)c10*(1.0 - x_factor); 339 const double x_result_1 = (double)c01*x_factor + (double)c11*(1.0 - x_factor); 340 const double result = x_result_0*y_factor + x_result_1*(1.0 - y_factor); 341 342 *newBuf++ = double_to_uchar_clamp255(result); 343 } 344 newBuf++; 345 } 346 newBuf += jdy; 347 } 348 } 349 350 void 351 ffdraw(Image *img) 352 { 353 int xoffset = (xw.w - img->ximg->width) / 2; 354 int yoffset = (xw.h - img->ximg->height) / 2; 355 XPutImage(xw.dpy, xw.win, d->gc, img->ximg, 0, 0, 356 xoffset, yoffset, img->ximg->width, img->ximg->height); 357 XFlush(xw.dpy); 358 } 359 360 void 361 getfontsize(Slide *s, unsigned int *width, unsigned int *height) 362 { 363 int i, j; 364 unsigned int curw, newmax; 365 float lfac = linespacing * (s->linecount - 1) + 1; 366 367 /* fit height */ 368 for (j = NUMFONTSCALES - 1; j >= 0; j--) 369 if (fonts[j]->h * lfac <= xw.uh) 370 break; 371 LIMIT(j, 0, NUMFONTSCALES - 1); 372 drw_setfontset(d, fonts[j]); 373 374 /* fit width */ 375 *width = 0; 376 for (i = 0; i < s->linecount; i++) { 377 curw = drw_fontset_getwidth(d, s->lines[i]); 378 newmax = (curw >= *width); 379 while (j > 0 && curw > xw.uw) { 380 drw_setfontset(d, fonts[--j]); 381 curw = drw_fontset_getwidth(d, s->lines[i]); 382 } 383 if (newmax) 384 *width = curw; 385 } 386 *height = fonts[j]->h * lfac; 387 } 388 389 void 390 cleanup(int slidesonly) 391 { 392 unsigned int i, j; 393 394 if (!slidesonly) { 395 for (i = 0; i < NUMFONTSCALES; i++) 396 drw_fontset_free(fonts[i]); 397 free(sc); 398 drw_free(d); 399 400 XDestroyWindow(xw.dpy, xw.win); 401 XSync(xw.dpy, False); 402 XCloseDisplay(xw.dpy); 403 } 404 405 if (slides) { 406 for (i = 0; i < slidecount; i++) { 407 for (j = 0; j < slides[i].linecount; j++) 408 free(slides[i].lines[j]); 409 free(slides[i].lines); 410 if (slides[i].img) 411 fffree(slides[i].img); 412 } 413 if (!slidesonly) { 414 free(slides); 415 slides = NULL; 416 } 417 } 418 } 419 420 void 421 reload(const Arg *arg) 422 { 423 FILE *fp = NULL; 424 unsigned int i; 425 426 if (!fname) { 427 fprintf(stderr, "sent: Cannot reload from stdin. Use a file!\n"); 428 return; 429 } 430 431 cleanup(1); 432 slidecount = 0; 433 434 if (!(fp = fopen(fname, "r"))) 435 die("sent: Unable to open '%s' for reading:", fname); 436 load(fp); 437 fclose(fp); 438 439 LIMIT(idx, 0, slidecount-1); 440 for (i = 0; i < slidecount; i++) 441 ffload(&slides[i]); 442 xdraw(); 443 } 444 445 void 446 load(FILE *fp) 447 { 448 static size_t size = 0; 449 size_t blen, maxlines; 450 char buf[BUFSIZ], *p; 451 Slide *s; 452 453 /* read each line from fp and add it to the item list */ 454 while (1) { 455 /* eat consecutive empty lines */ 456 while ((p = fgets(buf, sizeof(buf), fp))) 457 if (strcmp(buf, "\n") != 0 && buf[0] != '#') 458 break; 459 if (!p) 460 break; 461 462 if ((slidecount+1) * sizeof(*slides) >= size) 463 if (!(slides = realloc(slides, (size += BUFSIZ)))) 464 die("sent: Unable to reallocate %u bytes:", size); 465 466 /* read one slide */ 467 maxlines = 0; 468 memset((s = &slides[slidecount]), 0, sizeof(Slide)); 469 do { 470 /* if there's a leading null, we can't do blen-1 */ 471 if (buf[0] == '\0') 472 continue; 473 474 if (buf[0] == '#') 475 continue; 476 477 /* grow lines array */ 478 if (s->linecount >= maxlines) { 479 maxlines = 2 * s->linecount + 1; 480 if (!(s->lines = realloc(s->lines, maxlines * sizeof(s->lines[0])))) 481 die("sent: Unable to reallocate %u bytes:", maxlines * sizeof(s->lines[0])); 482 } 483 484 blen = strlen(buf); 485 if (!(s->lines[s->linecount] = strdup(buf))) 486 die("sent: Unable to strdup:"); 487 if (s->lines[s->linecount][blen-1] == '\n') 488 s->lines[s->linecount][blen-1] = '\0'; 489 490 /* mark as image slide if first line of a slide starts with @ */ 491 if (s->linecount == 0 && s->lines[0][0] == '@') 492 s->embed = &s->lines[0][1]; 493 494 if (s->lines[s->linecount][0] == '\\') 495 memmove(s->lines[s->linecount], &s->lines[s->linecount][1], blen); 496 s->linecount++; 497 } while ((p = fgets(buf, sizeof(buf), fp)) && strcmp(buf, "\n") != 0); 498 499 slidecount++; 500 if (!p) 501 break; 502 } 503 504 if (!slidecount) 505 die("sent: No slides in file"); 506 } 507 508 void 509 advance(const Arg *arg) 510 { 511 int new_idx = idx + arg->i; 512 LIMIT(new_idx, 0, slidecount-1); 513 if (new_idx != idx) { 514 if (slides[idx].img) 515 slides[idx].img->state &= ~SCALED; 516 idx = new_idx; 517 xdraw(); 518 } 519 } 520 521 void 522 quit(const Arg *arg) 523 { 524 running = 0; 525 } 526 527 void 528 resize(int width, int height) 529 { 530 xw.w = width; 531 xw.h = height; 532 xw.uw = usablewidth * width; 533 xw.uh = usableheight * height; 534 drw_resize(d, width, height); 535 } 536 537 void 538 run() 539 { 540 XEvent ev; 541 542 /* Waiting for window mapping */ 543 while (1) { 544 XNextEvent(xw.dpy, &ev); 545 if (ev.type == ConfigureNotify) { 546 resize(ev.xconfigure.width, ev.xconfigure.height); 547 } else if (ev.type == MapNotify) { 548 break; 549 } 550 } 551 552 while (running) { 553 XNextEvent(xw.dpy, &ev); 554 if (handler[ev.type]) 555 (handler[ev.type])(&ev); 556 } 557 } 558 559 void 560 xdraw() 561 { 562 unsigned int height, width, i; 563 Image *im = slides[idx].img; 564 565 getfontsize(&slides[idx], &width, &height); 566 XClearWindow(xw.dpy, xw.win); 567 568 if (!im) { 569 drw_rect(d, 0, 0, xw.w, xw.h, 1, 1); 570 for (i = 0; i < slides[idx].linecount; i++) 571 drw_text(d, 572 (xw.w - width) / 2, 573 (xw.h - height) / 2 + i * linespacing * d->fonts->h, 574 width, 575 d->fonts->h, 576 0, 577 slides[idx].lines[i], 578 0); 579 if (idx != 0 && progressheight != 0) { 580 drw_rect(d, 581 0, xw.h - progressheight, 582 (xw.w * idx)/(slidecount - 1), progressheight, 583 1, 0); 584 } 585 drw_map(d, xw.win, 0, 0, xw.w, xw.h); 586 } else { 587 if (!(im->state & SCALED)) 588 ffprepare(im); 589 ffdraw(im); 590 } 591 } 592 593 void 594 xhints() 595 { 596 XClassHint class = {.res_name = "sent", .res_class = "presenter"}; 597 XWMHints wm = {.flags = InputHint, .input = True}; 598 XSizeHints *sizeh = NULL; 599 600 if (!(sizeh = XAllocSizeHints())) 601 die("sent: Unable to allocate size hints"); 602 603 sizeh->flags = PSize; 604 sizeh->height = xw.h; 605 sizeh->width = xw.w; 606 607 XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class); 608 XFree(sizeh); 609 } 610 611 void 612 xinit() 613 { 614 XTextProperty prop; 615 unsigned int i; 616 617 if (!(xw.dpy = XOpenDisplay(NULL))) 618 die("sent: Unable to open display"); 619 xw.scr = XDefaultScreen(xw.dpy); 620 xw.vis = XDefaultVisual(xw.dpy, xw.scr); 621 resize(DisplayWidth(xw.dpy, xw.scr), DisplayHeight(xw.dpy, xw.scr)); 622 623 xw.attrs.bit_gravity = CenterGravity; 624 xw.attrs.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask | 625 ButtonMotionMask | ButtonPressMask; 626 627 xw.win = XCreateWindow(xw.dpy, XRootWindow(xw.dpy, xw.scr), 0, 0, 628 xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), 629 InputOutput, xw.vis, CWBitGravity | CWEventMask, 630 &xw.attrs); 631 632 xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); 633 xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); 634 XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); 635 636 if (!(d = drw_create(xw.dpy, xw.scr, xw.win, xw.w, xw.h))) 637 die("sent: Unable to create drawing context"); 638 sc = drw_scm_create(d, colors, 2); 639 drw_setscheme(d, sc); 640 XSetWindowBackground(xw.dpy, xw.win, sc[ColBg].pixel); 641 642 xloadfonts(); 643 for (i = 0; i < slidecount; i++) 644 ffload(&slides[i]); 645 646 XStringListToTextProperty(&argv0, 1, &prop); 647 XSetWMName(xw.dpy, xw.win, &prop); 648 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); 649 XFree(prop.value); 650 XMapWindow(xw.dpy, xw.win); 651 xhints(); 652 XSync(xw.dpy, False); 653 } 654 655 void 656 xloadfonts() 657 { 658 int i, j; 659 char *fstrs[LEN(fontfallbacks)]; 660 661 for (j = 0; j < LEN(fontfallbacks); j++) { 662 fstrs[j] = ecalloc(1, MAXFONTSTRLEN); 663 } 664 665 for (i = 0; i < NUMFONTSCALES; i++) { 666 for (j = 0; j < LEN(fontfallbacks); j++) { 667 if (MAXFONTSTRLEN < snprintf(fstrs[j], MAXFONTSTRLEN, "%s:size=%d", fontfallbacks[j], FONTSZ(i))) 668 die("sent: Font string too long"); 669 } 670 if (!(fonts[i] = drw_fontset_create(d, (const char**)fstrs, LEN(fstrs)))) 671 die("sent: Unable to load any font for size %d", FONTSZ(i)); 672 } 673 674 for (j = 0; j < LEN(fontfallbacks); j++) 675 if (fstrs[j]) 676 free(fstrs[j]); 677 } 678 679 void 680 bpress(XEvent *e) 681 { 682 unsigned int i; 683 684 for (i = 0; i < LEN(mshortcuts); i++) 685 if (e->xbutton.button == mshortcuts[i].b && mshortcuts[i].func) 686 mshortcuts[i].func(&(mshortcuts[i].arg)); 687 } 688 689 void 690 cmessage(XEvent *e) 691 { 692 if (e->xclient.data.l[0] == xw.wmdeletewin) 693 running = 0; 694 } 695 696 void 697 expose(XEvent *e) 698 { 699 if (0 == e->xexpose.count) 700 xdraw(); 701 } 702 703 void 704 kpress(XEvent *e) 705 { 706 unsigned int i; 707 KeySym sym; 708 709 sym = XkbKeycodeToKeysym(xw.dpy, (KeyCode)e->xkey.keycode, 0, 0); 710 for (i = 0; i < LEN(shortcuts); i++) 711 if (sym == shortcuts[i].keysym && shortcuts[i].func) 712 shortcuts[i].func(&(shortcuts[i].arg)); 713 } 714 715 void 716 configure(XEvent *e) 717 { 718 resize(e->xconfigure.width, e->xconfigure.height); 719 if (slides[idx].img) 720 slides[idx].img->state &= ~SCALED; 721 xdraw(); 722 } 723 724 void 725 usage() 726 { 727 die("usage: %s [-c fgcolor] [-b bgcolor] [-f font] [file]", argv0); 728 } 729 730 int 731 main(int argc, char *argv[]) 732 { 733 FILE *fp = NULL; 734 735 ARGBEGIN { 736 case 'v': 737 fprintf(stderr, "sent-"VERSION"\n"); 738 return 0; 739 case 'f': 740 fontfallbacks[0] = EARGF(usage()); 741 break; 742 case 'c': 743 colors[0] = EARGF(usage()); 744 break; 745 case 'b': 746 colors[1] = EARGF(usage()); 747 break; 748 default: 749 usage(); 750 } ARGEND 751 752 if (!argv[0] || !strcmp(argv[0], "-")) 753 fp = stdin; 754 else if (!(fp = fopen(fname = argv[0], "r"))) 755 die("sent: Unable to open '%s' for reading:", fname); 756 load(fp); 757 fclose(fp); 758 759 xinit(); 760 run(); 761 762 cleanup(0); 763 return 0; 764 }