├── .gitignore ├── README.md ├── devdraw.c ├── devdraw.h ├── drawclient.c ├── latin1.c ├── mkfile ├── mklatinkbd.c ├── mouseswap.c ├── nowsys.c ├── sixel.c └── winsize.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | o.* 3 | latin1.h 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sixel support for plan9port devdraw 2 | ====================================== 3 | 4 | *Sixeldraw* is an implementation of plan9port's *devdraw*(1) that runs inside a terminal and uses sixel for drawing and DEC Locator for mouse inputs. 5 | To use it just point `DEVDRAW` to the *sixeldraw* binary and run some *draw*(3) program, e.g. 6 | 7 | DEVDRAW=sixeldraw sam 8 | 9 | If `SIXELDBG=` is set to a file then *sixeldraw* debug messages are sent there. 10 | 11 | There is a known bug where *sixeldraw* will leave the terminal in a messy state after it exits. This is race condition between *sixeldraw* cleaning up and *sh*(1) reading the tty settings and is hard to fix. 12 | An easy workaround is to append `sleep 0.1` to the command. 13 | 14 | By default *sixeldraw* maintains its own snarf buffer. 15 | If `SNARF=1` is set, it uses the *xterm* sequences to read and write the clipboard, which may need to be enabled in the terminal emulator configuration. 16 | 17 | *Draw*(3) operations that change the current window's size, location etc. are currently ignored. 18 | Changing the cursor could be supported with a softcursor but isn't implemented yet. 19 | 20 | Terminal support 21 | ----------------- 22 | 23 | - Recent (!) *xterm* compiled with `--enable-sixel-graphics --enable-dec-locator` and with Xresources settings along the lines of 24 | 25 | XTerm*decTerminalID: vt340 26 | XTerm*numColorRegisters: 256 27 | XTerm*maxGraphicSize: 2560x1440 28 | 29 | For snarf support the setting is something like 30 | 31 | XTerm*disallowedWindowOps: 20,21,SetXprop 32 | 33 | - *Mlterm* needs `QUIRKS=3` to work around two bugs: The "sixel scrolling" mode is inverted and the order of mouse buttons in the DEC locator response is wrong. 34 | -------------------------------------------------------------------------------- /devdraw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * /dev/draw simulator -- handles the messages prepared by the draw library. 3 | * Doesn't simulate the file system part, just the messages. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "devdraw.h" 12 | 13 | extern void _flushmemscreen(Rectangle); 14 | int forcedpi = 0; 15 | int displaydpi = 100; 16 | 17 | #define NHASH (1<<5) 18 | #define HASHMASK (NHASH-1) 19 | 20 | typedef struct Client Client; 21 | typedef struct Draw Draw; 22 | typedef struct DImage DImage; 23 | typedef struct DScreen DScreen; 24 | typedef struct CScreen CScreen; 25 | typedef struct FChar FChar; 26 | typedef struct Refresh Refresh; 27 | typedef struct Refx Refx; 28 | typedef struct DName DName; 29 | 30 | struct Draw 31 | { 32 | QLock lk; 33 | int clientid; 34 | int nclient; 35 | Client* client[1]; 36 | int nname; 37 | DName* name; 38 | int vers; 39 | int softscreen; 40 | }; 41 | 42 | struct Client 43 | { 44 | /*Ref r;*/ 45 | DImage* dimage[NHASH]; 46 | CScreen* cscreen; 47 | Refresh* refresh; 48 | Rendez refrend; 49 | uchar* readdata; 50 | int nreaddata; 51 | int busy; 52 | int clientid; 53 | int slot; 54 | int refreshme; 55 | int infoid; 56 | int op; 57 | }; 58 | 59 | struct Refresh 60 | { 61 | DImage* dimage; 62 | Rectangle r; 63 | Refresh* next; 64 | }; 65 | 66 | struct Refx 67 | { 68 | Client* client; 69 | DImage* dimage; 70 | }; 71 | 72 | struct DName 73 | { 74 | char *name; 75 | Client *client; 76 | DImage* dimage; 77 | int vers; 78 | }; 79 | 80 | struct FChar 81 | { 82 | int minx; /* left edge of bits */ 83 | int maxx; /* right edge of bits */ 84 | uchar miny; /* first non-zero scan-line */ 85 | uchar maxy; /* last non-zero scan-line + 1 */ 86 | schar left; /* offset of baseline */ 87 | uchar width; /* width of baseline */ 88 | }; 89 | 90 | /* 91 | * Reference counts in DImages: 92 | * one per open by original client 93 | * one per screen image or fill 94 | * one per image derived from this one by name 95 | */ 96 | struct DImage 97 | { 98 | int id; 99 | int ref; 100 | char *name; 101 | int vers; 102 | Memimage* image; 103 | int ascent; 104 | int nfchar; 105 | FChar* fchar; 106 | DScreen* dscreen; /* 0 if not a window */ 107 | DImage* fromname; /* image this one is derived from, by name */ 108 | DImage* next; 109 | }; 110 | 111 | struct CScreen 112 | { 113 | DScreen* dscreen; 114 | CScreen* next; 115 | }; 116 | 117 | struct DScreen 118 | { 119 | int id; 120 | int public; 121 | int ref; 122 | DImage *dimage; 123 | DImage *dfill; 124 | Memscreen* screen; 125 | Client* owner; 126 | DScreen* next; 127 | }; 128 | 129 | static Draw sdraw; 130 | static Client *client0; 131 | static Memimage *screenimage; 132 | static Rectangle flushrect; 133 | static int waste; 134 | static DScreen* dscreen; 135 | static int drawuninstall(Client*, int); 136 | static Memimage* drawinstall(Client*, int, Memimage*, DScreen*); 137 | static void drawfreedimage(DImage*); 138 | 139 | void 140 | _initdisplaymemimage(Memimage *m) 141 | { 142 | screenimage = m; 143 | m->screenref = 1; 144 | client0 = mallocz(sizeof(Client), 1); 145 | if(client0 == nil){ 146 | fprint(2, "initdraw: allocating client0: out of memory"); 147 | abort(); 148 | } 149 | client0->slot = 0; 150 | client0->clientid = ++sdraw.clientid; 151 | client0->op = SoverD; 152 | sdraw.client[0] = client0; 153 | sdraw.nclient = 1; 154 | sdraw.softscreen = 1; 155 | } 156 | 157 | void 158 | _drawreplacescreenimage(Memimage *m) 159 | { 160 | /* 161 | * Replace the screen image because the screen 162 | * was resized. 163 | * 164 | * In theory there should only be one reference 165 | * to the current screen image, and that's through 166 | * client0's image 0, installed a few lines above. 167 | * Once the client drops the image, the underlying backing 168 | * store freed properly. The client is being notified 169 | * about the resize through external means, so all we 170 | * need to do is this assignment. 171 | */ 172 | Memimage *om; 173 | 174 | qlock(&sdraw.lk); 175 | om = screenimage; 176 | screenimage = m; 177 | m->screenref = 1; 178 | if(om && --om->screenref == 0){ 179 | _freememimage(om); 180 | } 181 | qunlock(&sdraw.lk); 182 | } 183 | 184 | static 185 | void 186 | drawrefreshscreen(DImage *l, Client *client) 187 | { 188 | while(l != nil && l->dscreen == nil) 189 | l = l->fromname; 190 | if(l != nil && l->dscreen->owner != client) 191 | l->dscreen->owner->refreshme = 1; 192 | } 193 | 194 | static 195 | void 196 | drawrefresh(Memimage *m, Rectangle r, void *v) 197 | { 198 | Refx *x; 199 | DImage *d; 200 | Client *c; 201 | Refresh *ref; 202 | 203 | USED(m); 204 | 205 | if(v == 0) 206 | return; 207 | x = v; 208 | c = x->client; 209 | d = x->dimage; 210 | for(ref=c->refresh; ref; ref=ref->next) 211 | if(ref->dimage == d){ 212 | combinerect(&ref->r, r); 213 | return; 214 | } 215 | ref = mallocz(sizeof(Refresh), 1); 216 | if(ref){ 217 | ref->dimage = d; 218 | ref->r = r; 219 | ref->next = c->refresh; 220 | c->refresh = ref; 221 | } 222 | } 223 | 224 | static void 225 | addflush(Rectangle r) 226 | { 227 | int abb, ar, anbb; 228 | Rectangle nbb; 229 | 230 | if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) 231 | return; 232 | 233 | if(flushrect.min.x >= flushrect.max.x){ 234 | flushrect = r; 235 | waste = 0; 236 | return; 237 | } 238 | nbb = flushrect; 239 | combinerect(&nbb, r); 240 | ar = Dx(r)*Dy(r); 241 | abb = Dx(flushrect)*Dy(flushrect); 242 | anbb = Dx(nbb)*Dy(nbb); 243 | /* 244 | * Area of new waste is area of new bb minus area of old bb, 245 | * less the area of the new segment, which we assume is not waste. 246 | * This could be negative, but that's OK. 247 | */ 248 | waste += anbb-abb - ar; 249 | if(waste < 0) 250 | waste = 0; 251 | /* 252 | * absorb if: 253 | * total area is small 254 | * waste is less than half total area 255 | * rectangles touch 256 | */ 257 | if(anbb<=1024 || waste*2layer; 284 | if(l == nil) 285 | return; 286 | do{ 287 | if(l->screen->image->data != screenimage->data) 288 | return; 289 | r = rectaddpt(r, l->delta); 290 | l = l->screen->image->layer; 291 | }while(l); 292 | addflush(r); 293 | } 294 | 295 | static 296 | void 297 | drawflush(void) 298 | { 299 | if(flushrect.min.x < flushrect.max.x) 300 | _flushmemscreen(flushrect); 301 | flushrect = Rect(10000, 10000, -10000, -10000); 302 | } 303 | 304 | static 305 | int 306 | drawcmp(char *a, char *b, int n) 307 | { 308 | if(strlen(a) != n) 309 | return 1; 310 | return memcmp(a, b, n); 311 | } 312 | 313 | static 314 | DName* 315 | drawlookupname(int n, char *str) 316 | { 317 | DName *name, *ename; 318 | 319 | name = sdraw.name; 320 | ename = &name[sdraw.nname]; 321 | for(; namename, str, n) == 0) 323 | return name; 324 | return 0; 325 | } 326 | 327 | static 328 | int 329 | drawgoodname(DImage *d) 330 | { 331 | DName *n; 332 | 333 | /* if window, validate the screen's own images */ 334 | if(d->dscreen) 335 | if(drawgoodname(d->dscreen->dimage) == 0 336 | || drawgoodname(d->dscreen->dfill) == 0) 337 | return 0; 338 | if(d->name == nil) 339 | return 1; 340 | n = drawlookupname(strlen(d->name), d->name); 341 | if(n==nil || n->vers!=d->vers) 342 | return 0; 343 | return 1; 344 | } 345 | 346 | static 347 | DImage* 348 | drawlookup(Client *client, int id, int checkname) 349 | { 350 | DImage *d; 351 | 352 | d = client->dimage[id&HASHMASK]; 353 | while(d){ 354 | if(d->id == id){ 355 | /* 356 | * BUG: should error out but too hard. 357 | * Return 0 instead. 358 | */ 359 | if(checkname && !drawgoodname(d)) 360 | return 0; 361 | return d; 362 | } 363 | d = d->next; 364 | } 365 | return 0; 366 | } 367 | 368 | static 369 | DScreen* 370 | drawlookupdscreen(int id) 371 | { 372 | DScreen *s; 373 | 374 | s = dscreen; 375 | while(s){ 376 | if(s->id == id) 377 | return s; 378 | s = s->next; 379 | } 380 | return 0; 381 | } 382 | 383 | static 384 | DScreen* 385 | drawlookupscreen(Client *client, int id, CScreen **cs) 386 | { 387 | CScreen *s; 388 | 389 | s = client->cscreen; 390 | while(s){ 391 | if(s->dscreen->id == id){ 392 | *cs = s; 393 | return s->dscreen; 394 | } 395 | s = s->next; 396 | } 397 | /* caller must check! */ 398 | return 0; 399 | } 400 | 401 | static 402 | Memimage* 403 | drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) 404 | { 405 | DImage *d; 406 | 407 | d = mallocz(sizeof(DImage), 1); 408 | if(d == 0) 409 | return 0; 410 | d->id = id; 411 | d->ref = 1; 412 | d->name = 0; 413 | d->vers = 0; 414 | d->image = i; 415 | if(i->screenref) 416 | ++i->screenref; 417 | d->nfchar = 0; 418 | d->fchar = 0; 419 | d->fromname = 0; 420 | d->dscreen = dscreen; 421 | d->next = client->dimage[id&HASHMASK]; 422 | client->dimage[id&HASHMASK] = d; 423 | return i; 424 | } 425 | 426 | static 427 | Memscreen* 428 | drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) 429 | { 430 | Memscreen *s; 431 | CScreen *c; 432 | 433 | c = mallocz(sizeof(CScreen), 1); 434 | if(dimage && dimage->image && dimage->image->chan == 0){ 435 | fprint(2, "bad image %p in drawinstallscreen", dimage->image); 436 | abort(); 437 | } 438 | 439 | if(c == 0) 440 | return 0; 441 | if(d == 0){ 442 | d = mallocz(sizeof(DScreen), 1); 443 | if(d == 0){ 444 | free(c); 445 | return 0; 446 | } 447 | s = mallocz(sizeof(Memscreen), 1); 448 | if(s == 0){ 449 | free(c); 450 | free(d); 451 | return 0; 452 | } 453 | s->frontmost = 0; 454 | s->rearmost = 0; 455 | d->dimage = dimage; 456 | if(dimage){ 457 | s->image = dimage->image; 458 | dimage->ref++; 459 | } 460 | d->dfill = dfill; 461 | if(dfill){ 462 | s->fill = dfill->image; 463 | dfill->ref++; 464 | } 465 | d->ref = 0; 466 | d->id = id; 467 | d->screen = s; 468 | d->public = public; 469 | d->next = dscreen; 470 | d->owner = client; 471 | dscreen = d; 472 | } 473 | c->dscreen = d; 474 | d->ref++; 475 | c->next = client->cscreen; 476 | client->cscreen = c; 477 | return d->screen; 478 | } 479 | 480 | static 481 | void 482 | drawdelname(DName *name) 483 | { 484 | int i; 485 | 486 | i = name-sdraw.name; 487 | memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); 488 | sdraw.nname--; 489 | } 490 | 491 | static 492 | void 493 | drawfreedscreen(DScreen *this) 494 | { 495 | DScreen *ds, *next; 496 | 497 | this->ref--; 498 | if(this->ref < 0) 499 | fprint(2, "negative ref in drawfreedscreen\n"); 500 | if(this->ref > 0) 501 | return; 502 | ds = dscreen; 503 | if(ds == this){ 504 | dscreen = this->next; 505 | goto Found; 506 | } 507 | while(next = ds->next){ /* assign = */ 508 | if(next == this){ 509 | ds->next = this->next; 510 | goto Found; 511 | } 512 | ds = next; 513 | } 514 | /* 515 | * Should signal Enodrawimage, but too hard. 516 | */ 517 | return; 518 | 519 | Found: 520 | if(this->dimage) 521 | drawfreedimage(this->dimage); 522 | if(this->dfill) 523 | drawfreedimage(this->dfill); 524 | free(this->screen); 525 | free(this); 526 | } 527 | 528 | static 529 | void 530 | drawfreedimage(DImage *dimage) 531 | { 532 | int i; 533 | Memimage *l; 534 | DScreen *ds; 535 | 536 | dimage->ref--; 537 | if(dimage->ref < 0) 538 | fprint(2, "negative ref in drawfreedimage\n"); 539 | if(dimage->ref > 0) 540 | return; 541 | 542 | /* any names? */ 543 | for(i=0; ifromname){ /* acquired by name; owned by someone else*/ 549 | drawfreedimage(dimage->fromname); 550 | goto Return; 551 | } 552 | ds = dimage->dscreen; 553 | l = dimage->image; 554 | dimage->dscreen = nil; /* paranoia */ 555 | dimage->image = nil; 556 | if(ds){ 557 | if(l->data == screenimage->data) 558 | addflush(l->layer->screenr); 559 | if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ 560 | free(l->layer->refreshptr); 561 | l->layer->refreshptr = nil; 562 | if(drawgoodname(dimage)) 563 | memldelete(l); 564 | else 565 | memlfree(l); 566 | drawfreedscreen(ds); 567 | }else{ 568 | if(l->screenref==0) 569 | freememimage(l); 570 | else if(--l->screenref==0) 571 | _freememimage(l); 572 | } 573 | Return: 574 | free(dimage->fchar); 575 | free(dimage); 576 | } 577 | 578 | static 579 | void 580 | drawuninstallscreen(Client *client, CScreen *this) 581 | { 582 | CScreen *cs, *next; 583 | 584 | cs = client->cscreen; 585 | if(cs == this){ 586 | client->cscreen = this->next; 587 | drawfreedscreen(this->dscreen); 588 | free(this); 589 | return; 590 | } 591 | while(next = cs->next){ /* assign = */ 592 | if(next == this){ 593 | cs->next = this->next; 594 | drawfreedscreen(this->dscreen); 595 | free(this); 596 | return; 597 | } 598 | cs = next; 599 | } 600 | } 601 | 602 | static 603 | int 604 | drawuninstall(Client *client, int id) 605 | { 606 | DImage *d, **l; 607 | 608 | for(l=&client->dimage[id&HASHMASK]; (d=*l) != nil; l=&d->next){ 609 | if(d->id == id){ 610 | *l = d->next; 611 | drawfreedimage(d); 612 | return 0; 613 | } 614 | } 615 | return -1; 616 | } 617 | 618 | static 619 | int 620 | drawaddname(Client *client, DImage *di, int n, char *str, char **err) 621 | { 622 | DName *name, *ename, *new, *t; 623 | char *ns; 624 | 625 | name = sdraw.name; 626 | ename = &name[sdraw.nname]; 627 | for(; namename, str, n) == 0){ 629 | *err = "image name in use"; 630 | return -1; 631 | } 632 | t = mallocz((sdraw.nname+1)*sizeof(DName), 1); 633 | ns = malloc(n+1); 634 | if(t == nil || ns == nil){ 635 | free(t); 636 | free(ns); 637 | *err = "out of memory"; 638 | return -1; 639 | } 640 | memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); 641 | free(sdraw.name); 642 | sdraw.name = t; 643 | new = &sdraw.name[sdraw.nname++]; 644 | new->name = ns; 645 | memmove(new->name, str, n); 646 | new->name[n] = 0; 647 | new->dimage = di; 648 | new->client = client; 649 | new->vers = ++sdraw.vers; 650 | return 0; 651 | } 652 | 653 | static int 654 | drawclientop(Client *cl) 655 | { 656 | int op; 657 | 658 | op = cl->op; 659 | cl->op = SoverD; 660 | return op; 661 | } 662 | 663 | static 664 | Memimage* 665 | drawimage(Client *client, uchar *a) 666 | { 667 | DImage *d; 668 | 669 | d = drawlookup(client, BGLONG(a), 1); 670 | if(d == nil) 671 | return nil; /* caller must check! */ 672 | return d->image; 673 | } 674 | 675 | static 676 | void 677 | drawrectangle(Rectangle *r, uchar *a) 678 | { 679 | r->min.x = BGLONG(a+0*4); 680 | r->min.y = BGLONG(a+1*4); 681 | r->max.x = BGLONG(a+2*4); 682 | r->max.y = BGLONG(a+3*4); 683 | } 684 | 685 | static 686 | void 687 | drawpoint(Point *p, uchar *a) 688 | { 689 | p->x = BGLONG(a+0*4); 690 | p->y = BGLONG(a+1*4); 691 | } 692 | 693 | static 694 | Point 695 | drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) 696 | { 697 | FChar *fc; 698 | Rectangle r; 699 | Point sp1; 700 | 701 | fc = &font->fchar[index]; 702 | r.min.x = p.x+fc->left; 703 | r.min.y = p.y-(font->ascent-fc->miny); 704 | r.max.x = r.min.x+(fc->maxx-fc->minx); 705 | r.max.y = r.min.y+(fc->maxy-fc->miny); 706 | sp1.x = sp->x+fc->left; 707 | sp1.y = sp->y+fc->miny; 708 | memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); 709 | p.x += fc->width; 710 | sp->x += fc->width; 711 | return p; 712 | } 713 | 714 | static 715 | uchar* 716 | drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) 717 | { 718 | int b, x; 719 | 720 | if(p >= maxp) 721 | return nil; 722 | b = *p++; 723 | x = b & 0x7F; 724 | if(b & 0x80){ 725 | if(p+1 >= maxp) 726 | return nil; 727 | x |= *p++ << 7; 728 | x |= *p++ << 15; 729 | if(x & (1<<22)) 730 | x |= ~0U<<23; 731 | }else{ 732 | if(b & 0x40) 733 | x |= ~0U<<7; 734 | x += oldx; 735 | } 736 | *newx = x; 737 | return p; 738 | } 739 | 740 | int 741 | _drawmsgread(void *a, int n) 742 | { 743 | Client *cl; 744 | 745 | qlock(&sdraw.lk); 746 | cl = client0; 747 | if(cl->readdata == nil){ 748 | werrstr("no draw data"); 749 | goto err; 750 | } 751 | if(n < cl->nreaddata){ 752 | werrstr("short read"); 753 | goto err; 754 | } 755 | n = cl->nreaddata; 756 | memmove(a, cl->readdata, cl->nreaddata); 757 | free(cl->readdata); 758 | cl->readdata = nil; 759 | qunlock(&sdraw.lk); 760 | return n; 761 | 762 | err: 763 | qunlock(&sdraw.lk); 764 | return -1; 765 | } 766 | 767 | int 768 | _drawmsgwrite(void *v, int n) 769 | { 770 | char cbuf[40], *err, ibuf[12*12+1], *s; 771 | int c, ci, doflush, dstid, e0, e1, esize, j, m; 772 | int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y; 773 | uchar *a, refresh, *u; 774 | u32int chan, value; 775 | Client *client; 776 | CScreen *cs; 777 | DImage *di, *ddst, *dsrc, *font, *ll; 778 | DName *dn; 779 | DScreen *dscrn; 780 | FChar *fc; 781 | Fmt fmt; 782 | Memimage *dst, *i, *l, **lp, *mask, *src; 783 | Memscreen *scrn; 784 | Point p, *pp, q, sp; 785 | Rectangle clipr, r; 786 | Refreshfn reffn; 787 | Refx *refx; 788 | 789 | qlock(&sdraw.lk); 790 | a = v; 791 | m = 0; 792 | oldn = n; 793 | client = client0; 794 | 795 | while((n-=m) > 0){ 796 | a += m; 797 | /*fprint(2, "msgwrite %d(%d)...", n, *a); */ 798 | switch(*a){ 799 | default: 800 | /*fprint(2, "bad command %d\n", *a); */ 801 | err = "bad draw command"; 802 | goto error; 803 | 804 | /* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] 805 | R[4*4] clipR[4*4] rrggbbaa[4] 806 | */ 807 | case 'b': 808 | m = 1+4+4+1+4+1+4*4+4*4+4; 809 | if(n < m) 810 | goto Eshortdraw; 811 | dstid = BGLONG(a+1); 812 | scrnid = BGSHORT(a+5); 813 | refresh = a[9]; 814 | chan = BGLONG(a+10); 815 | repl = a[14]; 816 | drawrectangle(&r, a+15); 817 | drawrectangle(&clipr, a+31); 818 | value = BGLONG(a+47); 819 | if(drawlookup(client, dstid, 0)) 820 | goto Eimageexists; 821 | if(scrnid){ 822 | dscrn = drawlookupscreen(client, scrnid, &cs); 823 | if(!dscrn) 824 | goto Enodrawscreen; 825 | scrn = dscrn->screen; 826 | if(repl || chan!=scrn->image->chan){ 827 | err = "image parameters incompatibile with screen"; 828 | goto error; 829 | } 830 | reffn = 0; 831 | switch(refresh){ 832 | case Refbackup: 833 | break; 834 | case Refnone: 835 | reffn = memlnorefresh; 836 | break; 837 | case Refmesg: 838 | reffn = drawrefresh; 839 | break; 840 | default: 841 | err = "unknown refresh method"; 842 | goto error; 843 | } 844 | l = memlalloc(scrn, r, reffn, 0, value); 845 | if(l == 0) 846 | goto Edrawmem; 847 | addflush(l->layer->screenr); 848 | l->clipr = clipr; 849 | rectclip(&l->clipr, r); 850 | if(drawinstall(client, dstid, l, dscrn) == 0){ 851 | memldelete(l); 852 | goto Edrawmem; 853 | } 854 | dscrn->ref++; 855 | if(reffn){ 856 | refx = nil; 857 | if(reffn == drawrefresh){ 858 | refx = mallocz(sizeof(Refx), 1); 859 | if(refx == 0){ 860 | if(drawuninstall(client, dstid) < 0) 861 | goto Enodrawimage; 862 | goto Edrawmem; 863 | } 864 | refx->client = client; 865 | refx->dimage = drawlookup(client, dstid, 1); 866 | } 867 | memlsetrefresh(l, reffn, refx); 868 | } 869 | continue; 870 | } 871 | i = allocmemimage(r, chan); 872 | if(i == 0) 873 | goto Edrawmem; 874 | if(repl) 875 | i->flags |= Frepl; 876 | i->clipr = clipr; 877 | if(!repl) 878 | rectclip(&i->clipr, r); 879 | if(drawinstall(client, dstid, i, 0) == 0){ 880 | freememimage(i); 881 | goto Edrawmem; 882 | } 883 | memfillcolor(i, value); 884 | continue; 885 | 886 | /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ 887 | case 'A': 888 | m = 1+4+4+4+1; 889 | if(n < m) 890 | goto Eshortdraw; 891 | dstid = BGLONG(a+1); 892 | if(dstid == 0) 893 | goto Ebadarg; 894 | if(drawlookupdscreen(dstid)) 895 | goto Escreenexists; 896 | ddst = drawlookup(client, BGLONG(a+5), 1); 897 | dsrc = drawlookup(client, BGLONG(a+9), 1); 898 | if(ddst==0 || dsrc==0) 899 | goto Enodrawimage; 900 | if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) 901 | goto Edrawmem; 902 | continue; 903 | 904 | /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ 905 | case 'c': 906 | m = 1+4+1+4*4; 907 | if(n < m) 908 | goto Eshortdraw; 909 | ddst = drawlookup(client, BGLONG(a+1), 1); 910 | if(ddst == nil) 911 | goto Enodrawimage; 912 | if(ddst->name){ 913 | err = "can't change repl/clipr of shared image"; 914 | goto error; 915 | } 916 | dst = ddst->image; 917 | if(a[5]) 918 | dst->flags |= Frepl; 919 | drawrectangle(&dst->clipr, a+6); 920 | continue; 921 | 922 | /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ 923 | case 'd': 924 | m = 1+4+4+4+4*4+2*4+2*4; 925 | if(n < m) 926 | goto Eshortdraw; 927 | dst = drawimage(client, a+1); 928 | dstid = BGLONG(a+1); 929 | src = drawimage(client, a+5); 930 | mask = drawimage(client, a+9); 931 | if(!dst || !src || !mask) 932 | goto Enodrawimage; 933 | drawrectangle(&r, a+13); 934 | drawpoint(&p, a+29); 935 | drawpoint(&q, a+37); 936 | op = drawclientop(client); 937 | memdraw(dst, r, src, p, mask, q, op); 938 | dstflush(dstid, dst, r); 939 | continue; 940 | 941 | /* toggle debugging: 'D' val[1] */ 942 | case 'D': 943 | m = 1+1; 944 | if(n < m) 945 | goto Eshortdraw; 946 | drawdebug = a[1]; 947 | continue; 948 | 949 | /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ 950 | case 'e': 951 | case 'E': 952 | m = 1+4+4+2*4+4+4+4+2*4+2*4; 953 | if(n < m) 954 | goto Eshortdraw; 955 | dst = drawimage(client, a+1); 956 | dstid = BGLONG(a+1); 957 | src = drawimage(client, a+5); 958 | if(!dst || !src) 959 | goto Enodrawimage; 960 | drawpoint(&p, a+9); 961 | e0 = BGLONG(a+17); 962 | e1 = BGLONG(a+21); 963 | if(e0<0 || e1<0){ 964 | err = "invalid ellipse semidiameter"; 965 | goto error; 966 | } 967 | j = BGLONG(a+25); 968 | if(j < 0){ 969 | err = "negative ellipse thickness"; 970 | goto error; 971 | } 972 | 973 | drawpoint(&sp, a+29); 974 | c = j; 975 | if(*a == 'E') 976 | c = -1; 977 | ox = BGLONG(a+37); 978 | oy = BGLONG(a+41); 979 | op = drawclientop(client); 980 | /* high bit indicates arc angles are present */ 981 | if(ox & ((ulong)1<<31)){ 982 | if((ox & ((ulong)1<<30)) == 0) 983 | ox &= ~((ulong)1<<31); 984 | memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); 985 | }else 986 | memellipse(dst, p, e0, e1, c, src, sp, op); 987 | dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); 988 | continue; 989 | 990 | /* free: 'f' id[4] */ 991 | case 'f': 992 | m = 1+4; 993 | if(n < m) 994 | goto Eshortdraw; 995 | ll = drawlookup(client, BGLONG(a+1), 0); 996 | if(ll && ll->dscreen && ll->dscreen->owner != client) 997 | ll->dscreen->owner->refreshme = 1; 998 | if(drawuninstall(client, BGLONG(a+1)) < 0) 999 | goto Enodrawimage; 1000 | continue; 1001 | 1002 | /* free screen: 'F' id[4] */ 1003 | case 'F': 1004 | m = 1+4; 1005 | if(n < m) 1006 | goto Eshortdraw; 1007 | if(!drawlookupscreen(client, BGLONG(a+1), &cs)) 1008 | goto Enodrawscreen; 1009 | drawuninstallscreen(client, cs); 1010 | continue; 1011 | 1012 | /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ 1013 | case 'i': 1014 | m = 1+4+4+1; 1015 | if(n < m) 1016 | goto Eshortdraw; 1017 | dstid = BGLONG(a+1); 1018 | if(dstid == 0){ 1019 | err = "can't use display as font"; 1020 | goto error; 1021 | } 1022 | font = drawlookup(client, dstid, 1); 1023 | if(font == 0) 1024 | goto Enodrawimage; 1025 | if(font->image->layer){ 1026 | err = "can't use window as font"; 1027 | goto error; 1028 | } 1029 | ni = BGLONG(a+5); 1030 | if(ni<=0 || ni>4096){ 1031 | err = "bad font size (4096 chars max)"; 1032 | goto error; 1033 | } 1034 | free(font->fchar); /* should we complain if non-zero? */ 1035 | font->fchar = mallocz(ni*sizeof(FChar), 1); 1036 | if(font->fchar == 0){ 1037 | err = "no memory for font"; 1038 | goto error; 1039 | } 1040 | memset(font->fchar, 0, ni*sizeof(FChar)); 1041 | font->nfchar = ni; 1042 | font->ascent = a[9]; 1043 | continue; 1044 | 1045 | /* set image 0 to screen image */ 1046 | case 'J': 1047 | m = 1; 1048 | if(n < m) 1049 | goto Eshortdraw; 1050 | if(drawlookup(client, 0, 0)) 1051 | goto Eimageexists; 1052 | drawinstall(client, 0, screenimage, 0); 1053 | client->infoid = 0; 1054 | continue; 1055 | 1056 | /* get image info: 'I' */ 1057 | case 'I': 1058 | m = 1; 1059 | if(n < m) 1060 | goto Eshortdraw; 1061 | if(client->infoid < 0) 1062 | goto Enodrawimage; 1063 | if(client->infoid == 0){ 1064 | i = screenimage; 1065 | if(i == nil) 1066 | goto Enodrawimage; 1067 | }else{ 1068 | di = drawlookup(client, client->infoid, 1); 1069 | if(di == nil) 1070 | goto Enodrawimage; 1071 | i = di->image; 1072 | } 1073 | ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d %11d" 1074 | " %11d %11d %11d %11d ", 1075 | client->clientid, 1076 | client->infoid, 1077 | chantostr(cbuf, i->chan), 1078 | (i->flags&Frepl)==Frepl, 1079 | i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, 1080 | i->clipr.min.x, i->clipr.min.y, 1081 | i->clipr.max.x, i->clipr.max.y); 1082 | free(client->readdata); 1083 | client->readdata = malloc(ni); 1084 | if(client->readdata == nil) 1085 | goto Enomem; 1086 | memmove(client->readdata, ibuf, ni); 1087 | client->nreaddata = ni; 1088 | client->infoid = -1; 1089 | continue; 1090 | 1091 | /* query: 'Q' n[1] queryspec[n] */ 1092 | case 'q': 1093 | if(n < 2) 1094 | goto Eshortdraw; 1095 | m = 1+1+a[1]; 1096 | if(n < m) 1097 | goto Eshortdraw; 1098 | fmtstrinit(&fmt); 1099 | for(c=0; creaddata = (uchar*)fmtstrflush(&fmt); 1113 | if(client->readdata == nil) 1114 | goto Enomem; 1115 | client->nreaddata = strlen((char*)client->readdata); 1116 | continue; 1117 | 1118 | /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ 1119 | case 'l': 1120 | m = 1+4+4+2+4*4+2*4+1+1; 1121 | if(n < m) 1122 | goto Eshortdraw; 1123 | font = drawlookup(client, BGLONG(a+1), 1); 1124 | if(font == 0) 1125 | goto Enodrawimage; 1126 | if(font->nfchar == 0) 1127 | goto Enotfont; 1128 | src = drawimage(client, a+5); 1129 | if(!src) 1130 | goto Enodrawimage; 1131 | ci = BGSHORT(a+9); 1132 | if(ci >= font->nfchar) 1133 | goto Eindex; 1134 | drawrectangle(&r, a+11); 1135 | drawpoint(&p, a+27); 1136 | memdraw(font->image, r, src, p, memopaque, p, S); 1137 | fc = &font->fchar[ci]; 1138 | fc->minx = r.min.x; 1139 | fc->maxx = r.max.x; 1140 | fc->miny = r.min.y; 1141 | fc->maxy = r.max.y; 1142 | fc->left = a[35]; 1143 | fc->width = a[36]; 1144 | continue; 1145 | 1146 | /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ 1147 | case 'L': 1148 | m = 1+4+2*4+2*4+4+4+4+4+2*4; 1149 | if(n < m) 1150 | goto Eshortdraw; 1151 | dst = drawimage(client, a+1); 1152 | dstid = BGLONG(a+1); 1153 | drawpoint(&p, a+5); 1154 | drawpoint(&q, a+13); 1155 | e0 = BGLONG(a+21); 1156 | e1 = BGLONG(a+25); 1157 | j = BGLONG(a+29); 1158 | if(j < 0){ 1159 | err = "negative line width"; 1160 | goto error; 1161 | } 1162 | src = drawimage(client, a+33); 1163 | if(!dst || !src) 1164 | goto Enodrawimage; 1165 | drawpoint(&sp, a+37); 1166 | op = drawclientop(client); 1167 | memline(dst, p, q, e0, e1, j, src, sp, op); 1168 | /* avoid memlinebbox if possible */ 1169 | if(dstid==0 || dst->layer!=nil){ 1170 | /* BUG: this is terribly inefficient: update maximal containing rect*/ 1171 | r = memlinebbox(p, q, e0, e1, j); 1172 | dstflush(dstid, dst, insetrect(r, -(1+1+j))); 1173 | } 1174 | continue; 1175 | 1176 | /* create image mask: 'm' newid[4] id[4] */ 1177 | /* 1178 | * 1179 | case 'm': 1180 | m = 4+4; 1181 | if(n < m) 1182 | goto Eshortdraw; 1183 | break; 1184 | * 1185 | */ 1186 | 1187 | /* attach to a named image: 'n' dstid[4] j[1] name[j] */ 1188 | case 'n': 1189 | m = 1+4+1; 1190 | if(n < m) 1191 | goto Eshortdraw; 1192 | j = a[5]; 1193 | if(j == 0) /* give me a non-empty name please */ 1194 | goto Eshortdraw; 1195 | m += j; 1196 | if(n < m) 1197 | goto Eshortdraw; 1198 | dstid = BGLONG(a+1); 1199 | if(drawlookup(client, dstid, 0)) 1200 | goto Eimageexists; 1201 | dn = drawlookupname(j, (char*)a+6); 1202 | if(dn == nil) 1203 | goto Enoname; 1204 | s = malloc(j+1); 1205 | if(s == nil) 1206 | goto Enomem; 1207 | if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) 1208 | goto Edrawmem; 1209 | di = drawlookup(client, dstid, 0); 1210 | if(di == 0) 1211 | goto Eoldname; 1212 | di->vers = dn->vers; 1213 | di->name = s; 1214 | di->fromname = dn->dimage; 1215 | di->fromname->ref++; 1216 | memmove(di->name, a+6, j); 1217 | di->name[j] = 0; 1218 | client->infoid = dstid; 1219 | continue; 1220 | 1221 | /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ 1222 | case 'N': 1223 | m = 1+4+1+1; 1224 | if(n < m) 1225 | goto Eshortdraw; 1226 | c = a[5]; 1227 | j = a[6]; 1228 | if(j == 0) /* give me a non-empty name please */ 1229 | goto Eshortdraw; 1230 | m += j; 1231 | if(n < m) 1232 | goto Eshortdraw; 1233 | di = drawlookup(client, BGLONG(a+1), 0); 1234 | if(di == 0) 1235 | goto Enodrawimage; 1236 | if(di->name) 1237 | goto Enamed; 1238 | if(c) 1239 | if(drawaddname(client, di, j, (char*)a+7, &err) < 0) 1240 | goto error; 1241 | else{ 1242 | dn = drawlookupname(j, (char*)a+7); 1243 | if(dn == nil) 1244 | goto Enoname; 1245 | if(dn->dimage != di) 1246 | goto Ewrongname; 1247 | drawdelname(dn); 1248 | } 1249 | continue; 1250 | 1251 | /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ 1252 | case 'o': 1253 | m = 1+4+2*4+2*4; 1254 | if(n < m) 1255 | goto Eshortdraw; 1256 | dst = drawimage(client, a+1); 1257 | if(!dst) 1258 | goto Enodrawimage; 1259 | if(dst->layer){ 1260 | drawpoint(&p, a+5); 1261 | drawpoint(&q, a+13); 1262 | r = dst->layer->screenr; 1263 | ni = memlorigin(dst, p, q); 1264 | if(ni < 0){ 1265 | err = "image origin failed"; 1266 | goto error; 1267 | } 1268 | if(ni > 0){ 1269 | addflush(r); 1270 | addflush(dst->layer->screenr); 1271 | ll = drawlookup(client, BGLONG(a+1), 1); 1272 | drawrefreshscreen(ll, client); 1273 | } 1274 | } 1275 | continue; 1276 | 1277 | /* set compositing operator for next draw operation: 'O' op */ 1278 | case 'O': 1279 | m = 1+1; 1280 | if(n < m) 1281 | goto Eshortdraw; 1282 | client->op = a[1]; 1283 | continue; 1284 | 1285 | /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1286 | /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1287 | case 'p': 1288 | case 'P': 1289 | m = 1+4+2+4+4+4+4+2*4; 1290 | if(n < m) 1291 | goto Eshortdraw; 1292 | dstid = BGLONG(a+1); 1293 | dst = drawimage(client, a+1); 1294 | ni = BGSHORT(a+5); 1295 | if(ni < 0){ 1296 | err = "negative cout in polygon"; 1297 | goto error; 1298 | } 1299 | e0 = BGLONG(a+7); 1300 | e1 = BGLONG(a+11); 1301 | j = 0; 1302 | if(*a == 'p'){ 1303 | j = BGLONG(a+15); 1304 | if(j < 0){ 1305 | err = "negative polygon line width"; 1306 | goto error; 1307 | } 1308 | } 1309 | src = drawimage(client, a+19); 1310 | if(!dst || !src) 1311 | goto Enodrawimage; 1312 | drawpoint(&sp, a+23); 1313 | drawpoint(&p, a+31); 1314 | ni++; 1315 | pp = mallocz(ni*sizeof(Point), 1); 1316 | if(pp == nil) 1317 | goto Enomem; 1318 | doflush = 0; 1319 | if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) 1320 | doflush = 1; /* simplify test in loop */ 1321 | ox = oy = 0; 1322 | esize = 0; 1323 | u = a+m; 1324 | for(y=0; y esize) 1341 | esize = c; 1342 | } 1343 | if(y == ni-1){ 1344 | c = memlineendsize(e1); 1345 | if(c > esize) 1346 | esize = c; 1347 | } 1348 | } 1349 | if(*a=='P' && e0!=1 && e0 !=~0) 1350 | r = dst->clipr; 1351 | else if(y > 0){ 1352 | r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); 1353 | combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1354 | } 1355 | if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ 1356 | dstflush(dstid, dst, r); 1357 | } 1358 | pp[y] = p; 1359 | } 1360 | if(y == 1) 1361 | dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1362 | op = drawclientop(client); 1363 | if(*a == 'p') 1364 | mempoly(dst, pp, ni, e0, e1, j, src, sp, op); 1365 | else 1366 | memfillpoly(dst, pp, ni, e0, src, sp, op); 1367 | free(pp); 1368 | m = u-a; 1369 | continue; 1370 | 1371 | /* read: 'r' id[4] R[4*4] */ 1372 | case 'r': 1373 | m = 1+4+4*4; 1374 | if(n < m) 1375 | goto Eshortdraw; 1376 | i = drawimage(client, a+1); 1377 | if(!i) 1378 | goto Enodrawimage; 1379 | drawrectangle(&r, a+5); 1380 | if(!rectinrect(r, i->r)) 1381 | goto Ereadoutside; 1382 | c = bytesperline(r, i->depth); 1383 | c *= Dy(r); 1384 | free(client->readdata); 1385 | client->readdata = mallocz(c, 0); 1386 | if(client->readdata == nil){ 1387 | err = "readimage malloc failed"; 1388 | goto error; 1389 | } 1390 | client->nreaddata = memunload(i, r, client->readdata, c); 1391 | if(client->nreaddata < 0){ 1392 | free(client->readdata); 1393 | client->readdata = nil; 1394 | err = "bad readimage call"; 1395 | goto error; 1396 | } 1397 | continue; 1398 | 1399 | /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ 1400 | /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ 1401 | case 's': 1402 | case 'x': 1403 | m = 1+4+4+4+2*4+4*4+2*4+2; 1404 | if(*a == 'x') 1405 | m += 4+2*4; 1406 | if(n < m) 1407 | goto Eshortdraw; 1408 | 1409 | dst = drawimage(client, a+1); 1410 | dstid = BGLONG(a+1); 1411 | src = drawimage(client, a+5); 1412 | if(!dst || !src) 1413 | goto Enodrawimage; 1414 | font = drawlookup(client, BGLONG(a+9), 1); 1415 | if(font == 0) 1416 | goto Enodrawimage; 1417 | if(font->nfchar == 0) 1418 | goto Enotfont; 1419 | drawpoint(&p, a+13); 1420 | drawrectangle(&r, a+21); 1421 | drawpoint(&sp, a+37); 1422 | ni = BGSHORT(a+45); 1423 | u = a+m; 1424 | m += ni*2; 1425 | if(n < m) 1426 | goto Eshortdraw; 1427 | clipr = dst->clipr; 1428 | dst->clipr = r; 1429 | op = drawclientop(client); 1430 | if(*a == 'x'){ 1431 | /* paint background */ 1432 | l = drawimage(client, a+47); 1433 | if(!l) 1434 | goto Enodrawimage; 1435 | drawpoint(&q, a+51); 1436 | r.min.x = p.x; 1437 | r.min.y = p.y-font->ascent; 1438 | r.max.x = p.x; 1439 | r.max.y = r.min.y+Dy(font->image->r); 1440 | j = ni; 1441 | while(--j >= 0){ 1442 | ci = BGSHORT(u); 1443 | if(ci<0 || ci>=font->nfchar){ 1444 | dst->clipr = clipr; 1445 | goto Eindex; 1446 | } 1447 | r.max.x += font->fchar[ci].width; 1448 | u += 2; 1449 | } 1450 | memdraw(dst, r, l, q, memopaque, ZP, op); 1451 | u -= 2*ni; 1452 | } 1453 | q = p; 1454 | while(--ni >= 0){ 1455 | ci = BGSHORT(u); 1456 | if(ci<0 || ci>=font->nfchar){ 1457 | dst->clipr = clipr; 1458 | goto Eindex; 1459 | } 1460 | q = drawchar(dst, q, src, &sp, font, ci, op); 1461 | u += 2; 1462 | } 1463 | dst->clipr = clipr; 1464 | p.y -= font->ascent; 1465 | dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); 1466 | continue; 1467 | 1468 | /* use public screen: 'S' id[4] chan[4] */ 1469 | case 'S': 1470 | m = 1+4+4; 1471 | if(n < m) 1472 | goto Eshortdraw; 1473 | dstid = BGLONG(a+1); 1474 | if(dstid == 0) 1475 | goto Ebadarg; 1476 | dscrn = drawlookupdscreen(dstid); 1477 | if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) 1478 | goto Enodrawscreen; 1479 | if(dscrn->screen->image->chan != BGLONG(a+5)){ 1480 | err = "inconsistent chan"; 1481 | goto error; 1482 | } 1483 | if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) 1484 | goto Edrawmem; 1485 | continue; 1486 | 1487 | /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ 1488 | case 't': 1489 | m = 1+1+2; 1490 | if(n < m) 1491 | goto Eshortdraw; 1492 | nw = BGSHORT(a+2); 1493 | if(nw < 0) 1494 | goto Ebadarg; 1495 | if(nw == 0) 1496 | continue; 1497 | m += nw*4; 1498 | if(n < m) 1499 | goto Eshortdraw; 1500 | lp = mallocz(nw*sizeof(Memimage*), 1); 1501 | if(lp == 0) 1502 | goto Enomem; 1503 | for(j=0; jlayer == 0){ 1511 | err = "images are not windows"; 1512 | free(lp); 1513 | goto error; 1514 | } 1515 | for(j=1; jlayer->screen != lp[0]->layer->screen){ 1517 | err = "images not on same screen"; 1518 | free(lp); 1519 | goto error; 1520 | } 1521 | if(a[1]) 1522 | memltofrontn(lp, nw); 1523 | else 1524 | memltorearn(lp, nw); 1525 | if(lp[0]->layer->screen->image->data == screenimage->data) 1526 | for(j=0; jlayer->screenr); 1528 | free(lp); 1529 | ll = drawlookup(client, BGLONG(a+1+1+2), 1); 1530 | drawrefreshscreen(ll, client); 1531 | continue; 1532 | 1533 | /* visible: 'v' */ 1534 | case 'v': 1535 | m = 1; 1536 | drawflush(); 1537 | continue; 1538 | 1539 | /* write: 'y' id[4] R[4*4] data[x*1] */ 1540 | /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ 1541 | case 'y': 1542 | case 'Y': 1543 | m = 1+4+4*4; 1544 | if(n < m) 1545 | goto Eshortdraw; 1546 | dstid = BGLONG(a+1); 1547 | dst = drawimage(client, a+1); 1548 | if(!dst) 1549 | goto Enodrawimage; 1550 | drawrectangle(&r, a+5); 1551 | if(!rectinrect(r, dst->r)) 1552 | goto Ewriteoutside; 1553 | y = memload(dst, r, a+m, n-m, *a=='Y'); 1554 | if(y < 0){ 1555 | err = "bad writeimage call"; 1556 | goto error; 1557 | } 1558 | dstflush(dstid, dst, r); 1559 | m += y; 1560 | continue; 1561 | } 1562 | } 1563 | qunlock(&sdraw.lk); 1564 | return oldn - n; 1565 | 1566 | Enodrawimage: 1567 | err = "unknown id for draw image"; 1568 | goto error; 1569 | Enodrawscreen: 1570 | err = "unknown id for draw screen"; 1571 | goto error; 1572 | Eshortdraw: 1573 | err = "short draw message"; 1574 | goto error; 1575 | /* 1576 | Eshortread: 1577 | err = "draw read too short"; 1578 | goto error; 1579 | */ 1580 | Eimageexists: 1581 | err = "image id in use"; 1582 | goto error; 1583 | Escreenexists: 1584 | err = "screen id in use"; 1585 | goto error; 1586 | Edrawmem: 1587 | err = "image memory allocation failed"; 1588 | goto error; 1589 | Ereadoutside: 1590 | err = "readimage outside image"; 1591 | goto error; 1592 | Ewriteoutside: 1593 | err = "writeimage outside image"; 1594 | goto error; 1595 | Enotfont: 1596 | err = "image not a font"; 1597 | goto error; 1598 | Eindex: 1599 | err = "character index out of range"; 1600 | goto error; 1601 | /* 1602 | Enoclient: 1603 | err = "no such draw client"; 1604 | goto error; 1605 | Edepth: 1606 | err = "image has bad depth"; 1607 | goto error; 1608 | Enameused: 1609 | err = "image name in use"; 1610 | goto error; 1611 | */ 1612 | Enoname: 1613 | err = "no image with that name"; 1614 | goto error; 1615 | Eoldname: 1616 | err = "named image no longer valid"; 1617 | goto error; 1618 | Enamed: 1619 | err = "image already has name"; 1620 | goto error; 1621 | Ewrongname: 1622 | err = "wrong name for image"; 1623 | goto error; 1624 | Enomem: 1625 | err = "out of memory"; 1626 | goto error; 1627 | Ebadarg: 1628 | err = "bad argument in draw message"; 1629 | goto error; 1630 | 1631 | error: 1632 | werrstr("%s", err); 1633 | qunlock(&sdraw.lk); 1634 | return -1; 1635 | } 1636 | 1637 | 1638 | -------------------------------------------------------------------------------- /devdraw.h: -------------------------------------------------------------------------------- 1 | int _drawmsgread(void*, int); 2 | int _drawmsgwrite(void*, int); 3 | void _initdisplaymemimage(Memimage*); 4 | int _latin1(Rune*, int); 5 | int parsewinsize(char*, Rectangle*, int*); 6 | int mouseswap(int); 7 | void abortcompose(void); 8 | 9 | extern int displaydpi; 10 | extern int forcedpi; 11 | -------------------------------------------------------------------------------- /drawclient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct Cmd Cmd; 10 | struct Cmd { 11 | char *cmd; 12 | void (*fn)(int, char**); 13 | }; 14 | 15 | Biobuf b; 16 | int fd; 17 | uchar buf[64*1024]; 18 | 19 | void 20 | startsrv(void) 21 | { 22 | int pid, p[2]; 23 | 24 | if(pipe(p) < 0) 25 | sysfatal("pipe"); 26 | if((pid=fork()) < 0) 27 | sysfatal("fork"); 28 | if(pid == 0){ 29 | close(p[0]); 30 | dup(p[1], 0); 31 | dup(p[1], 1); 32 | execl("o.drawsrv", "o.drawsrv", "-D", nil); 33 | sysfatal("exec: %r"); 34 | } 35 | close(p[1]); 36 | fd = p[0]; 37 | } 38 | 39 | int 40 | domsg(Wsysmsg *m) 41 | { 42 | int n, nn; 43 | 44 | n = convW2M(m, buf, sizeof buf); 45 | fprint(2, "write %d to %d\n", n, fd); 46 | write(fd, buf, n); 47 | n = readwsysmsg(fd, buf, sizeof buf); 48 | nn = convM2W(buf, n, m); 49 | assert(nn == n); 50 | if(m->op == Rerror) 51 | return -1; 52 | return 0; 53 | } 54 | 55 | void 56 | cmdinit(int argc, char **argv) 57 | { 58 | Wsysmsg m; 59 | 60 | memset(&m, 0, sizeof m); 61 | m.op = Tinit; 62 | m.winsize = "100x100"; 63 | m.label = "label"; 64 | m.font = ""; 65 | if(domsg(&m) < 0) 66 | sysfatal("domsg"); 67 | } 68 | 69 | void 70 | cmdmouse(int argc, char **argv) 71 | { 72 | Wsysmsg m; 73 | 74 | memset(&m, 0, sizeof m); 75 | m.op = Trdmouse; 76 | if(domsg(&m) < 0) 77 | sysfatal("domsg"); 78 | print("%c %d %d %d\n", 79 | m.resized ? 'r' : 'm', 80 | m.mouse.xy.x, 81 | m.mouse.xy.y, 82 | m.mouse.buttons); 83 | } 84 | 85 | void 86 | cmdkbd(int argc, char **argv) 87 | { 88 | Wsysmsg m; 89 | 90 | memset(&m, 0, sizeof m); 91 | m.op = Trdkbd; 92 | if(domsg(&m) < 0) 93 | sysfatal("domsg"); 94 | print("%s\n", m.runes); 95 | } 96 | 97 | Cmd cmdtab[] = { 98 | { "init", cmdinit, }, 99 | { "mouse", cmdmouse, }, 100 | { "kbd", cmdkbd, }, 101 | }; 102 | 103 | void 104 | main(int argc, char **argv) 105 | { 106 | char *p, *f[20]; 107 | int i, nf; 108 | 109 | startsrv(); 110 | 111 | fprint(2, "started...\n"); 112 | Binit(&b, 0, OREAD); 113 | while((p = Brdstr(&b, '\n', 1)) != nil){ 114 | fprint(2, "%s...\n", p); 115 | nf = tokenize(p, f, nelem(f)); 116 | for(i=0; i 2 | #include 3 | #include 4 | 5 | /* 6 | * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a 7 | * prefix of latintab[j].ld only when j=5) 57 | return unicode(k); 58 | else 59 | return -5; 60 | } 61 | 62 | for(l=latintab; l->ld!=0; l++) 63 | if(k[0] == l->ld[0]){ 64 | if(n == 1) 65 | return -2; 66 | if(l->ld[1] == 0) 67 | c = k[1]; 68 | else if(l->ld[1] != k[1]) 69 | continue; 70 | else if(n == 2) 71 | return -3; 72 | else 73 | c = k[2]; 74 | for(p=l->si; *p!=0; p++) 75 | if(*p == c) 76 | return l->so[p - l->si]; 77 | return -1; 78 | } 79 | return -1; 80 | } 81 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | <$PLAN9/src/mkhdr 2 | <|osxvers 3 | 4 | TARG=sixeldraw 5 | 6 | WSYSOFILES=\ 7 | devdraw.$O\ 8 | latin1.$O\ 9 | mouseswap.$O\ 10 | winsize.$O\ 11 | 12 | OFILES=$WSYSOFILES sixel.$O 13 | 14 | HFILES=\ 15 | devdraw.h\ 16 | 17 | <$PLAN9/src/mkone 18 | 19 | $O.drawclient: drawclient.$O drawfcall.$O 20 | $LD -o $target $prereq 21 | 22 | $O.mklatinkbd: mklatinkbd.$O 23 | $LD -o $target $prereq 24 | 25 | latin1.$O: latin1.h 26 | 27 | latin1.h: $PLAN9/lib/keyboard $O.mklatinkbd 28 | ./$O.mklatinkbd -r $PLAN9/lib/keyboard | sed 's/, }/ }/' >$target 29 | 30 | CLEANFILES=$O.macargv $O.mklatinkbd latin1.h 31 | 32 | install: mklatinkbd.install 33 | install:Q: 34 | if [ $MACARGV ]; then 35 | mk $MKFLAGS macargv.install 36 | fi 37 | -------------------------------------------------------------------------------- /mklatinkbd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Parse /lib/keyboard to create latin1.h table for kernel. 3 | * mklatinkbd -r prints an array of integers rather than a Rune string literal. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int rflag; 12 | int xflag; 13 | 14 | enum { 15 | MAXLD = 2, /* latin1.c assumes this is 2 */ 16 | }; 17 | 18 | char *head = "" 19 | "/*\n" 20 | " * This is automatically generated by %s from /lib/keyboard\n" 21 | " * Edit /lib/keyboard instead.\n" 22 | " */\n"; 23 | 24 | /* 25 | * latin1.c assumes that strlen(ld) is at most 2. 26 | * It also assumes that latintab[i].ld can be a prefix of latintab[j].ld 27 | * only when j < i. We ensure this by sorting the output by prefix length. 28 | * The so array is indexed by the character value. 29 | */ 30 | 31 | typedef struct Trie Trie; 32 | struct Trie { 33 | int n; /* of characters r */ 34 | char seq[MAXLD+1+1]; 35 | Rune r[256]; 36 | Trie *link[256]; 37 | }; 38 | 39 | Trie *root; 40 | 41 | Trie* 42 | mktrie(char *seq) 43 | { 44 | uchar *q; 45 | Trie **tp; 46 | 47 | if(root == nil) { 48 | root = malloc(sizeof *root); 49 | memset(root, 0, sizeof *root); 50 | } 51 | 52 | assert(seq[0] != '\0'); 53 | 54 | tp = &root; 55 | for(q=(uchar*)seq; *(q+1) != '\0'; q++) { 56 | tp = &(*tp)->link[*q]; 57 | if(*tp == nil) { 58 | *tp = malloc(sizeof(**tp)); 59 | assert(*tp != nil); 60 | memset(*tp, 0, sizeof(**tp)); 61 | strcpy((*tp)->seq, seq); 62 | (*tp)->seq[q+1-(uchar*)seq] = '\0'; 63 | } 64 | } 65 | 66 | assert(*tp != nil); 67 | return *tp; 68 | } 69 | 70 | /* add character sequence s meaning rune r */ 71 | void 72 | insert(char *s, Rune r) 73 | { 74 | uchar lastc; 75 | int len; 76 | Trie *t; 77 | 78 | len = strlen(s); 79 | lastc = (uchar)s[len-1]; 80 | 81 | t = mktrie(s); 82 | if(t->r[lastc]) { 83 | fprint(2, "warning: table duplicate: %s is %C and %C\n", s, t->r[lastc], r); 84 | return; 85 | } 86 | t->r[lastc] = r; 87 | t->n++; 88 | } 89 | 90 | void 91 | cprintchar(Biobuf *b, int c) 92 | { 93 | /* print a byte c safe for a C string. */ 94 | switch(c) { 95 | case '\'': 96 | case '\"': 97 | case '\\': 98 | Bprint(b, "\\%c", c); 99 | break; 100 | case '\t': 101 | Bprint(b, "\\t"); 102 | break; 103 | default: 104 | if(isascii(c) && isprint(c)) 105 | Bprint(b, "%c", c); 106 | else 107 | Bprint(b, "\\x%.2x", c); 108 | break; 109 | } 110 | } 111 | 112 | void 113 | cprints(Biobuf *b, char *p) 114 | { 115 | while(*p != '\0') 116 | cprintchar(b, *p++); 117 | } 118 | 119 | void 120 | xprint(Biobuf *b, int c) 121 | { 122 | } 123 | 124 | void 125 | printtrie(Biobuf *b, Trie *t) 126 | { 127 | int i; 128 | char *p; 129 | 130 | for(i=0; i<256; i++) 131 | if(t->link[i]) 132 | printtrie(b, t->link[i]); 133 | if(t->n == 0) 134 | return; 135 | 136 | if(xflag) { 137 | for(i=0; i<256; i++) { 138 | if(t->r[i] == 0) 139 | continue; 140 | Bprint(b, ""); 141 | for(p=t->seq; *p; p++) 142 | Bprint(b, " %k", *p); 143 | Bprint(b, " %k : \"%C\" U%04X\n", i, t->r[i], t->r[i]); 144 | } 145 | return; 146 | } 147 | 148 | Bprint(b, "\t\""); 149 | cprints(b, t->seq); 150 | Bprint(b, "\", \""); 151 | for(i=0; i<256; i++) 152 | if(t->r[i]) 153 | cprintchar(b, i); 154 | Bprint(b, "\",\t"); 155 | if(rflag) { 156 | Bprint(b, "{"); 157 | for(i=0; i<256; i++) 158 | if(t->r[i]) 159 | Bprint(b, " 0x%.4ux,", t->r[i]); 160 | Bprint(b, " }"); 161 | } else { 162 | Bprint(b, "L\""); 163 | for(i=0; i<256; i++) 164 | if(t->r[i]) 165 | Bprint(b, "%C", t->r[i]); 166 | Bprint(b, "\""); 167 | } 168 | Bprint(b, ",\n"); 169 | } 170 | 171 | void 172 | readfile(char *fname) 173 | { 174 | Biobuf *b; 175 | char *line, *p; 176 | char *seq; 177 | int inseq; 178 | int lineno; 179 | Rune r; 180 | 181 | if((b = Bopen(fname, OREAD)) == 0) { 182 | fprint(2, "cannot open \"%s\": %r\n", fname); 183 | exits("open"); 184 | } 185 | 186 | lineno = 0; 187 | while((line = Brdline(b, '\n')) != 0) { 188 | lineno++; 189 | if(line[0] == '#') 190 | continue; 191 | 192 | r = strtol(line, nil, 16); 193 | p = strchr(line, ' '); 194 | if(r == 0 || p != line+4 || p[0] != ' ' || p[1] != ' ') { 195 | fprint(2, "%s:%d: cannot parse line\n", fname, lineno); 196 | continue; 197 | } 198 | 199 | p = line+6; 200 | /* 00AE Or rO ® registered trade mark sign */ 201 | for(inseq=1, seq=p; (uchar)*p < Runeself; p++) { 202 | if(*p == '\0' || isspace(*p)) { 203 | if(inseq && p-seq >= 2) { 204 | *p = '\0'; 205 | inseq = 0; 206 | insert(seq, r); 207 | *p = ' '; 208 | } 209 | if(*p == '\0') 210 | break; 211 | } else { 212 | if(!inseq) { 213 | seq = p; 214 | inseq = 1; 215 | } 216 | } 217 | } 218 | } 219 | } 220 | 221 | void 222 | usage(void) 223 | { 224 | fprint(2, "usage: mklatinkbd [-r] [/lib/keyboard]\n"); 225 | exits("usage"); 226 | } 227 | 228 | int kfmt(Fmt*); 229 | 230 | void 231 | main(int argc, char **argv) 232 | { 233 | int i; 234 | Biobuf bout; 235 | 236 | ARGBEGIN{ 237 | case 'r': /* print rune values */ 238 | rflag = 1; 239 | break; 240 | case 'x': 241 | xflag = 1; 242 | break; 243 | default: 244 | usage(); 245 | }ARGEND 246 | 247 | if(argc > 1) 248 | usage(); 249 | 250 | fmtinstall('k', kfmt); 251 | readfile(argc == 1 ? argv[0] : "/dev/stdin"); 252 | 253 | Binit(&bout, 1, OWRITE); 254 | if(xflag) { 255 | Bprint(&bout, "# Generated by mklatinkbd -x; do not edit.\n"); 256 | for(i=0x20; i<0x10000; i++) 257 | Bprint(&bout, " <%x> <%x> <%x> <%x> : \"%C\" U%04X\n", 258 | (i>>12)&0xf, (i>>8)&0xf, (i>>4)&0xf, i&0xf, i, i); 259 | } 260 | if(root) 261 | printtrie(&bout, root); 262 | exits(0); 263 | } 264 | 265 | // X11 key names 266 | 267 | struct { 268 | int c; 269 | char *s; 270 | } xkey[] = { 271 | ' ', "space", 272 | '!', "exclam", 273 | '"', "quotedbl", 274 | '#', "numbersign", 275 | '$', "dollar", 276 | '%', "percent", 277 | '&', "ampersand", 278 | '\'', "apostrophe", 279 | '(', "parenleft", 280 | ')', "parenright", 281 | '*', "asterisk", 282 | '+', "plus", 283 | ',', "comma", 284 | '-', "minus", 285 | '.', "period", 286 | '/', "slash", 287 | ':', "colon", 288 | ';', "semicolon", 289 | '<', "less", 290 | '=', "equal", 291 | '>', "greater", 292 | '?', "question", 293 | '@', "at", 294 | '[', "bracketleft", 295 | '\\', "backslash", 296 | ',', "bracketright", 297 | '^', "asciicircum", 298 | '_', "underscore", 299 | '`', "grave", 300 | '{', "braceleft", 301 | '|', "bar", 302 | '}', "braceright", 303 | '~', "asciitilde", 304 | 0, 0 305 | }; 306 | 307 | int 308 | kfmt(Fmt *f) 309 | { 310 | int i, c; 311 | 312 | c = va_arg(f->args, int); 313 | for(i=0; xkey[i].s; i++) 314 | if(xkey[i].c == c) 315 | return fmtprint(f, "<%s>", xkey[i].s); 316 | return fmtprint(f, "<%c>", c); 317 | } 318 | 319 | 320 | -------------------------------------------------------------------------------- /mouseswap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "devdraw.h" 6 | 7 | enum 8 | { 9 | Nbutton = 10 10 | }; 11 | 12 | static int debug; 13 | 14 | static struct 15 | { 16 | int b[Nbutton]; 17 | int init; 18 | } map; 19 | 20 | static void 21 | initmap(void) 22 | { 23 | char *p; 24 | int i; 25 | 26 | p = getenv("mousedebug"); 27 | if(p && p[0]) 28 | debug = atoi(p); 29 | 30 | for(i=0; i= 0) 58 | nbut |= 1< %#b\n", but, nbut); 61 | return nbut; 62 | } 63 | -------------------------------------------------------------------------------- /nowsys.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void 9 | usage(void) 10 | { 11 | fprint(2, "usage: devdraw (don't run directly)\n"); 12 | exits("usage"); 13 | } 14 | 15 | void 16 | main(int argc, char **argv) 17 | { 18 | int n; 19 | uchar buf[1024*1024]; 20 | Wsysmsg m; 21 | 22 | ARGBEGIN{ 23 | case 'D': 24 | break; 25 | default: 26 | usage(); 27 | }ARGEND 28 | 29 | if(argc != 0) 30 | usage(); 31 | 32 | while((n = readwsysmsg(0, buf, sizeof buf)) > 0){ 33 | convM2W(buf, n, &m); 34 | m.type = Rerror; 35 | m.error = "no window system present"; 36 | n = convW2M(&m, buf, sizeof buf); 37 | write(1, buf, n); 38 | } 39 | exits(0); 40 | } 41 | -------------------------------------------------------------------------------- /sixel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "devdraw.h" 17 | 18 | int tty; 19 | Biobuf *fdin; 20 | Biobuf *ttyin, *ttyout; 21 | Memimage *_img; 22 | Channel *msgout; 23 | Channel *flushch; 24 | Channel *cmdch; 25 | Channel *mousech; 26 | Channel *kbdch; 27 | Channel *snarfch; 28 | char *snarf; 29 | 30 | #define MOUSEREQ "\033['|" 31 | #define SIZEREQ "\033[14t" 32 | #define SETTITLE "\033]0;%s\033\\" 33 | #define WRSNARF "\033]52;s0;%.*[\033\\" 34 | #define RDSNARF "\033]52;s0;?\033\\" 35 | 36 | typedef struct Tagbuf Tagbuf; 37 | 38 | enum { MAXBUF = 2*MAXWMSG }; 39 | 40 | struct Tagbuf { 41 | int t[32]; 42 | int ri, wi; 43 | QLock ql; 44 | }; 45 | 46 | Tagbuf mousetags, keytags, snarftags; 47 | struct termios tiold; 48 | int oldpgrp; 49 | QLock sizelock; 50 | Rectangle window; 51 | QLock outlock; 52 | int inited, resized, ending; 53 | enum { 54 | QUIRKSIXSCR = 1, 55 | QUIRKMBSWAP = 2, 56 | } quirks; 57 | int snarfm; 58 | 59 | void 60 | ttyinit(void) 61 | { 62 | fprint(tty, "\033[?1049h"); /* use alternate screen buffer */ 63 | fprint(tty, "\033[1;1'z"); /* enable locator */ 64 | fprint(tty, "\033[1'{"); /* select locator events */ 65 | fprint(tty, "\033[?80%c", (quirks & QUIRKSIXSCR) != 0 ? 'h' : 'l'); /* reset sixel scrolling mode */ 66 | fprint(tty, "\033[?25l"); /* hide cursor */ 67 | fprint(tty, SIZEREQ); /* report xterm window size */ 68 | } 69 | 70 | void 71 | cleanup(void) 72 | { 73 | int i; 74 | 75 | if(ending++) return; 76 | fprint(2, "cleaning up\n"); 77 | for(i = 0; !canqlock(&outlock) && i < 10; i++) 78 | usleep(1000*100); 79 | if(i == 10) fprint(2, "failed to acquire outlock\n"); 80 | fprint(tty, "\033\\"); /* just to be sure */ 81 | fprint(tty, "\033[2J"); /* clear all */ 82 | fprint(tty, "\033[0'{"); /* no locator events */ 83 | fprint(tty, "\033[0;0'z"); /* disable locator */ 84 | fprint(tty, "\033[?25h"); /* show cursor */ 85 | fprint(tty, "\033[?1049l"); /* back to normal screen buffer */ 86 | if(tcsetattr(tty, TCSAFLUSH, &tiold) < 0) 87 | fprint(2, "tcsetattr: %r\n"); 88 | tcsetpgrp(tty, oldpgrp); 89 | } 90 | 91 | void 92 | rawmode(int fd) 93 | { 94 | struct termios ti; 95 | 96 | notedisable("sys: ttou"); 97 | setpgid(0, 0); 98 | oldpgrp = tcgetpgrp(fd); 99 | tcsetpgrp(fd, getpgid(0)); 100 | if(tcgetattr(fd, &tiold) < 0) sysfatal("tcgetattr"); 101 | ti = tiold; 102 | cfmakeraw(&ti); 103 | if(tcsetattr(fd, TCSAFLUSH, &ti) < 0) sysfatal("tcsetattr"); 104 | } 105 | 106 | void 107 | outproc(void *arg) 108 | { 109 | Wsysmsg w; 110 | static uchar buf[MAXWMSG]; 111 | int n; 112 | 113 | for(;;){ 114 | if(recv(msgout, &w) < 0) 115 | sysfatal("recv: %r"); 116 | n = convW2M(&w, buf, sizeof(buf)); 117 | if(n == 0) continue; 118 | if(write(4, buf, n) < n) 119 | sysfatal("write: %r"); 120 | 121 | } 122 | } 123 | 124 | void 125 | replymsg(Wsysmsg *m) 126 | { 127 | if((m->type & 1) == 0) 128 | m->type++; 129 | send(msgout, m); 130 | } 131 | 132 | void 133 | replyerror(Wsysmsg *m) 134 | { 135 | char err[ERRMAX]; 136 | 137 | rerrstr(err, sizeof(err)); 138 | m->type = Rerror; 139 | m->error = err; 140 | replymsg(m); 141 | } 142 | 143 | int 144 | havetag(Tagbuf *t) 145 | { 146 | return t->ri != t->wi; 147 | } 148 | 149 | int 150 | gettag(Tagbuf *t) 151 | { 152 | int r; 153 | 154 | r = t->t[t->ri++]; 155 | if(t->ri == nelem(t->t)) 156 | t->ri = 0; 157 | return r; 158 | } 159 | 160 | void 161 | puttag(Tagbuf *t, int v) 162 | { 163 | qlock(&t->ql); 164 | t->t[t->wi++] = v; 165 | if(t->wi == nelem(t->t)) 166 | t->wi = 0; 167 | if(t->wi == t->ri) 168 | sysfatal("too many queried operations"); 169 | qunlock(&t->ql); 170 | } 171 | 172 | void 173 | matchmouse(void) 174 | { 175 | Wsysmsg m; 176 | 177 | qlock(&mousetags.ql); 178 | while(havetag(&mousetags) && nbrecv(mousech, &m.mouse) > 0){ 179 | m.type = Rrdmouse; 180 | m.tag = gettag(&mousetags); 181 | m.resized = resized; 182 | resized = 0; 183 | replymsg(&m); 184 | } 185 | qunlock(&mousetags.ql); 186 | } 187 | 188 | void 189 | matchkbd(void) 190 | { 191 | Wsysmsg m; 192 | ulong ul; 193 | 194 | qlock(&keytags.ql); 195 | while(havetag(&keytags) && nbrecv(kbdch, &ul) > 0){ 196 | m.rune = ul; 197 | m.type = Rrdkbd; 198 | m.tag = gettag(&keytags); 199 | replymsg(&m); 200 | } 201 | qunlock(&keytags.ql); 202 | } 203 | 204 | void 205 | matchsnarf(void) 206 | { 207 | Wsysmsg m; 208 | char *p; 209 | 210 | qlock(&snarftags.ql); 211 | while(havetag(&snarftags) && nbrecv(snarfch, &p) > 0){ 212 | m.type = Rrdsnarf; 213 | m.tag = gettag(&snarftags); 214 | m.snarf = p; 215 | replymsg(&m); 216 | } 217 | qunlock(&snarftags.ql); 218 | } 219 | 220 | void 221 | runmsg(Wsysmsg *m) 222 | { 223 | static uchar buf[65536]; 224 | int n; 225 | 226 | switch(m->type){ 227 | case Trdmouse: 228 | puttag(&mousetags, m->tag); 229 | matchmouse(); 230 | break; 231 | case Trdkbd: 232 | puttag(&keytags, m->tag); 233 | matchkbd(); 234 | break; 235 | case Tinit: 236 | memimageinit(); 237 | qlock(&sizelock); 238 | _img = _allocmemimage(window, CMAP8); 239 | qunlock(&sizelock); 240 | _initdisplaymemimage(_img); 241 | replymsg(m); 242 | inited++; 243 | if(m->label != nil) 244 | sendp(cmdch, smprint(SETTITLE, m->label)); 245 | break; 246 | case Trddraw: 247 | n = m->count > sizeof(buf) ? sizeof(buf) : m->count; 248 | if(n = _drawmsgread(buf, n), n < 0) 249 | replyerror(m); 250 | else{ 251 | m->data = buf; 252 | m->count = n; 253 | replymsg(m); 254 | } 255 | break; 256 | case Twrdraw: 257 | if(_drawmsgwrite(m->data, m->count) < 0) 258 | replyerror(m); 259 | else 260 | replymsg(m); 261 | break; 262 | case Tlabel: 263 | sendp(cmdch, smprint(SETTITLE, m->label)); 264 | break; 265 | case Twrsnarf: 266 | free(snarf); 267 | switch(snarfm){ 268 | case 1: sendp(cmdch, smprint(WRSNARF, strlen(m->snarf), m->snarf)); break; 269 | default: snarf = strdup(m->snarf); 270 | } 271 | replymsg(m); 272 | break; 273 | case Trdsnarf: 274 | switch(snarfm){ 275 | case 1: sendp(cmdch, strdup(RDSNARF)); puttag(&snarftags, m->tag); matchsnarf(); break; 276 | default: m->snarf = snarf; replymsg(m); 277 | } 278 | break; 279 | default: 280 | fprint(2, "unknown message %W\n", m); 281 | case Tmoveto: 282 | case Tbouncemouse: 283 | m->type = Rerror; 284 | m->error = "not implemented"; 285 | replymsg(m); 286 | } 287 | } 288 | 289 | void 290 | msgproc(void *arg) 291 | { 292 | int rc; 293 | Wsysmsg w; 294 | static uchar buf[MAXWMSG]; 295 | uchar *p; 296 | 297 | p = buf; 298 | for(;;){ 299 | rc = Bgetc(fdin); 300 | if(rc < 0){ 301 | cleanup(); 302 | threadexitsall(nil); 303 | } 304 | *p++ = rc; 305 | rc = convM2W(buf, p - buf, &w); 306 | if(rc > 0){ 307 | runmsg(&w); 308 | p -= rc; 309 | } 310 | if(w.type == Rrdsnarf) 311 | free(w.snarf); 312 | } 313 | } 314 | 315 | int 316 | ansiparse(char *s, int *f, int nf, char **imm, char *fi) 317 | { 318 | int i, v; 319 | char *imms, fic; 320 | 321 | while(strchr("<=>?", *s) != nil) s++; 322 | for(i = 0; ; i++){ 323 | if(*s == ';'){ 324 | if(i < nf) 325 | f[i] = 0; 326 | s++; 327 | continue; 328 | } 329 | if(!isdigit(*s)) 330 | break; 331 | v = strtol(s, &s, 10); 332 | if(i < nf) 333 | f[i] = v; 334 | if(*s == ';') 335 | s++; 336 | } 337 | if(*s == 0) return -1; 338 | imms = s; 339 | while(*s >= 32 && *s <= 47) s++; 340 | fic = *s; 341 | if(*s < 64 || *s > 126) return -1; 342 | if(imm != nil) *imm = imms; 343 | if(fi != nil) *fi = fic; 344 | return i; 345 | } 346 | 347 | static Mouse curmouse; 348 | 349 | void 350 | decmouse(char *s) 351 | { 352 | int b; 353 | int f[4]; 354 | char *p; 355 | 356 | if(ansiparse(s, f, nelem(f), &p, nil) < 4 || strcmp(p, "&w") != 0) 357 | return; 358 | curmouse.xy.x = f[3]; 359 | curmouse.xy.y = f[2]; 360 | b = f[1]; 361 | if((quirks & QUIRKMBSWAP) == 0) 362 | b = b & ~7 | b << 2 & 4 | b & 2 | b >> 2 & 1; 363 | curmouse.buttons = b; 364 | nbsend(mousech, &curmouse); 365 | matchmouse(); 366 | } 367 | 368 | void 369 | windowsize(char *s) 370 | { 371 | int x, y, f[3]; 372 | char *p; 373 | 374 | if(ansiparse(s, f, nelem(f), &p, nil) < 3 || strcmp(p, "t") != 0) 375 | return; 376 | x = f[2]; 377 | y = (f[1] / 6 - 1) * 6; 378 | if(inited && window.max.x == x && window.max.y == y) return; 379 | window.max.x = x; 380 | window.max.y = y; 381 | if(!inited) 382 | qunlock(&sizelock); 383 | else{ 384 | _img = allocmemimage(window, CMAP8); 385 | _drawreplacescreenimage(_img); 386 | resized++; 387 | nbsend(mousech, &curmouse); 388 | matchmouse(); 389 | } 390 | } 391 | 392 | void 393 | safeputs(char *s) 394 | { 395 | for(; *s != 0; s++) 396 | if(isprint(*s)) 397 | fprint(2, "%c", *s); 398 | else 399 | fprint(2, "\\%#o", (uchar)*s); 400 | fprint(2, "\n"); 401 | } 402 | 403 | void 404 | snarfresp(char *s) 405 | { 406 | char *p, *q; 407 | int n, rc; 408 | char *v; 409 | 410 | s = strchr(s, ';'); 411 | if(s == nil) return; 412 | s = strchr(s+1, ';'); 413 | if(s == nil) return; 414 | s++; 415 | for(p = s, q = s; *p != 0;) 416 | if(*p == 033){ 417 | p++; 418 | if(*p == 0) break; 419 | if(*p == '['){ 420 | do 421 | p++; 422 | while(*p > 0 && (*p < 64 || *p > 126)); 423 | if(*p == 0) break; 424 | } 425 | p++; 426 | }else 427 | *q++ = *p++; 428 | *q = 0; 429 | 430 | n = (strlen(s) + 3) * 3 / 4 + 10; 431 | v = malloc(n); 432 | rc = dec64((uchar *) v, n - 1, s, strlen(s)); 433 | if(rc < 0){ 434 | fprint(2, "base64 decode failed: %s\n", s); 435 | *v = 0; 436 | }else{ 437 | v[rc] = 0; 438 | } 439 | nbsendp(snarfch, v); 440 | matchsnarf(); 441 | } 442 | 443 | Rune ss3keys[] = { 444 | ['A'] Kup, 445 | ['B'] Kdown, 446 | ['C'] Kleft, 447 | ['D'] Kright, 448 | [' '] ' ', 449 | ['I'] '\t', 450 | ['M'] '\r', 451 | ['P'] KF|1, 452 | ['Q'] KF|2, 453 | ['R'] KF|3, 454 | ['S'] KF|4, 455 | }; 456 | 457 | Rune csikeys[] = { 458 | [1] Khome, 459 | [2] Kins, 460 | [3] Kdel, 461 | [4] Kend, 462 | [5] Kpgup, 463 | [6] Kpgdown, 464 | [11] KF|1, 465 | [12] KF|2, 466 | [13] KF|3, 467 | [14] KF|4, 468 | [15] KF|5, 469 | [17] KF|6, 470 | [18] KF|7, 471 | [19] KF|8, 472 | [20] KF|9, 473 | [21] KF|10, 474 | [23] KF|11, 475 | [24] KF|12, 476 | }; 477 | 478 | static void 479 | kbdkey(int c) 480 | { 481 | static Rune buf[32]; 482 | static Rune *p; 483 | int r; 484 | Rune *q; 485 | 486 | if(c == Kalt) 487 | p = buf; 488 | else if(p != nil){ 489 | *p++ = c; 490 | *p = 0; 491 | r = _latin1(buf, p - buf); 492 | if(p == buf + sizeof(buf) - 1 || r == -1){ 493 | for(q = buf; q < p; q++) 494 | nbsendul(kbdch, *q); 495 | p = nil; 496 | }else if(r > 0){ 497 | nbsendul(kbdch, r); 498 | p = nil; 499 | } 500 | }else 501 | nbsendul(kbdch, c); 502 | } 503 | 504 | void 505 | ttyinproc(void *arg) 506 | { 507 | int c, r; 508 | int f[10]; 509 | static char buf[256]; 510 | char *p; 511 | Rune ru; 512 | 513 | for(;;){ 514 | c = Bgetc(ttyin); 515 | if(c < 0) threadexitsall(nil); 516 | switch(c){ 517 | case 033: 518 | c = Bgetc(ttyin); 519 | if(c == 'O'){ 520 | c = Bgetc(ttyin); 521 | if(c < 0) break; 522 | if(c >= nelem(ss3keys) || (r = ss3keys[c]) == 0) 523 | fprint(2, "unknown key SS3 %c\n", c); 524 | else 525 | kbdkey(r); 526 | break; 527 | } 528 | if(c == ']'){ 529 | p = Brdstr(ttyin, '\\', 1); 530 | if(strncmp(p, "52;", 3) == 0) 531 | snarfresp(p); 532 | free(p); 533 | break; 534 | } 535 | if(c != '['){ 536 | kbdkey(Kalt); 537 | kbdkey(c); 538 | break; 539 | } 540 | p = buf; 541 | while(c = Bgetc(ttyin), c >= 0){ 542 | if(p < buf + sizeof(buf) - 1) 543 | *p++ = c; 544 | if(c >= 64 && c <= 126) break; 545 | } 546 | *p = 0; 547 | switch(c){ 548 | case 'A': kbdkey(Kup); break; 549 | case 'B': kbdkey(Kdown); break; 550 | case 'C': kbdkey(Kright); break; 551 | case 'D': kbdkey(Kleft); break; 552 | case 'H': kbdkey(Khome); break; 553 | case 'F': kbdkey(Kend); break; 554 | case 'w': decmouse(buf); break; 555 | case 't': windowsize(buf); break; 556 | case '~': 557 | if(ansiparse(buf, f, nelem(f), nil, nil) < 1) 558 | break; 559 | if(f[0] >= nelem(csikeys) || (r = csikeys[f[0]]) == 0) 560 | fprint(2, "unknown key ESC %d ~\n", f[0]); 561 | else 562 | kbdkey(r); 563 | break; 564 | default: fprint(2, "unknown control %c\n", c); 565 | } 566 | break; 567 | case 13: 568 | kbdkey(10); 569 | break; 570 | default: 571 | if(c < 0) 572 | threadexitsall(nil); 573 | if(c >= 0x80){ 574 | p = buf; 575 | *p++ = c; 576 | while(!fullrune(buf, p - buf)){ 577 | c = Bgetc(ttyin); 578 | if(c < 0) break; 579 | *p++ = c; 580 | } 581 | chartorune(&ru, buf); 582 | kbdkey(ru); 583 | }else 584 | kbdkey(c); 585 | } 586 | matchkbd(); 587 | } 588 | } 589 | 590 | void 591 | flush(Rectangle r) 592 | { 593 | int i, x, y, c, b, v, m; 594 | int lastch, lastn; 595 | static int maxcol[256]; 596 | static uchar *col[6]; 597 | 598 | qlock(&outlock); 599 | restart: 600 | Bprint(ttyout, "\033Pq"); 601 | for(i = 0; i < 256; i++){ 602 | v = cmap2rgb(i); 603 | Bprint(ttyout, "#%d;2;%d;%d;%d", i, (v >> 16 & 0xff) * 100 / 255, (v >> 8 & 0xff) * 100 / 255, (v & 0xff) * 100 / 255); 604 | } 605 | if(r.max.y > _img->r.max.y) r.max.y = _img->r.max.y; 606 | r = _img->r; 607 | col[0] = malloc(_img->r.max.x * 6); 608 | for(i = 1; i < 6; i++) 609 | col[i] = col[i-1] + _img->r.max.x; 610 | for(y = 0; y < r.max.y; y += 6){ 611 | if(nbrecv(flushch, &r) > 0){ 612 | Bprint(ttyout, "\033\\"); 613 | free(col[0]); 614 | goto restart; 615 | } 616 | if(y + 5 < r.min.y) 617 | goto next; 618 | _unloadmemimage(_img, Rect(0, y, _img->r.max.x, y + 6), col[0], _img->r.max.x * 6); 619 | memset(maxcol, 0, sizeof maxcol); 620 | for(x = 0; x < Dx(_img->r); x++) 621 | for(i = 0; i < 6; i++) 622 | maxcol[col[i][x]] = x + 1; 623 | for(c = 0; c < 256; c++){ 624 | if(maxcol[c] == 0) continue; 625 | Bprint(ttyout, "#%d", c); 626 | lastch = -1; 627 | lastn = 0; 628 | for(x = 0; x < maxcol[c]; x++){ 629 | b = 0; 630 | m = ~63; 631 | for(i = 0; i < 6; i++){ 632 | if(col[i][x] == c) 633 | b |= 1<= 4) 641 | Bprint(ttyout, "!%d%c", lastn, 63 + lastch); 642 | else 643 | while(lastn--) 644 | Bputc(ttyout, 63 + lastch); 645 | lastn = 1; 646 | lastch = b; 647 | } 648 | } 649 | if(lastn >= 4) 650 | Bprint(ttyout, "!%d%c", lastn, 63 + lastch); 651 | else 652 | while(lastn--) 653 | Bputc(ttyout, 63 + lastch); 654 | Bputc(ttyout, '$'); 655 | } 656 | next: 657 | Bputc(ttyout, '-'); 658 | } 659 | free(col[0]); 660 | Bprint(ttyout, "\033\\"); 661 | Bflush(ttyout); 662 | qunlock(&outlock); 663 | } 664 | 665 | void 666 | ttyoutproc(void *arg) 667 | { 668 | Rectangle r; 669 | int rc; 670 | char *str; 671 | 672 | for(;;){ 673 | Alt a[] = { 674 | {flushch, &r, CHANRCV}, 675 | {cmdch, &str, CHANRCV}, 676 | {nil, nil, CHANEND} 677 | }; 678 | rc = alt(a); 679 | if(ending) return; 680 | switch(rc){ 681 | case 0: flush(r); break; 682 | case 1: 683 | qlock(&outlock); 684 | Bprint(ttyout, "%s", str); 685 | Bflush(ttyout); 686 | qunlock(&outlock); 687 | free(str); 688 | break; 689 | } 690 | } 691 | } 692 | 693 | void 694 | mousereqproc(void *arg) 695 | { 696 | int n; 697 | 698 | n = 0; 699 | for(;;){ 700 | usleep(20*1000); 701 | sendp(cmdch, strdup(MOUSEREQ)); 702 | if(++n == 10){ 703 | n = 0; 704 | sendp(cmdch, strdup(SIZEREQ)); 705 | } 706 | } 707 | } 708 | 709 | int 710 | notehand(void *ureg, char *note) 711 | { 712 | cleanup(); 713 | return 0; 714 | } 715 | 716 | void 717 | threadmain(int argc, char **argv) 718 | { 719 | fmtinstall('W', drawfcallfmt); 720 | fmtinstall('[', encodefmt); 721 | 722 | ARGBEGIN { 723 | default: break; 724 | } ARGEND; 725 | 726 | dup(0, 3); 727 | dup(1, 4); 728 | close(0); 729 | close(1); 730 | close(2); 731 | open("/dev/null", OREAD); 732 | open("/dev/null", OWRITE); 733 | if(getenv("SIXELDBG") != nil) 734 | open(getenv("SIXELDBG"), OWRITE); 735 | else 736 | open("/dev/null", OWRITE); 737 | 738 | if(getenv("QUIRKS") != nil) 739 | quirks = atoi(getenv("QUIRKS")); 740 | if(getenv("SNARF") != nil) 741 | snarfm = atoi(getenv("SNARF")); 742 | 743 | tty = open("/dev/tty", ORDWR); 744 | if(tty < 0) sysfatal("open: %r"); 745 | rawmode(tty); 746 | 747 | atexit(cleanup); 748 | ttyinit(); 749 | qlock(&sizelock); 750 | 751 | fdin = Bfdopen(3, OREAD); 752 | if(fdin == nil) sysfatal("Bfdopen: %r"); 753 | ttyin = Bfdopen(tty, OREAD); 754 | if(ttyin == nil) sysfatal("Bfdopen: %r"); 755 | ttyout = Bfdopen(tty, OWRITE); 756 | if(ttyout == nil) sysfatal("Bfdopen: %r"); 757 | msgout = chancreate(sizeof(Wsysmsg), 2); 758 | flushch = chancreate(sizeof(Rectangle), 0); 759 | cmdch = chancreate(sizeof(char *), 16); 760 | mousech = chancreate(sizeof(Mouse), 32); 761 | kbdch = chancreate(sizeof(ulong), 256); 762 | snarfch = chancreate(sizeof(char *), 32); 763 | 764 | threadnotify(notehand, 1); 765 | 766 | proccreate(outproc, nil, mainstacksize); 767 | proccreate(ttyinproc, nil, mainstacksize); 768 | proccreate(ttyoutproc, nil, mainstacksize); 769 | proccreate(mousereqproc, nil, mainstacksize); 770 | 771 | msgproc(nil); 772 | } 773 | 774 | void 775 | _flushmemscreen(Rectangle r) 776 | { 777 | send(flushch, &r); 778 | } 779 | 780 | int 781 | cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) 782 | { 783 | return _cloadmemimage(i, r, data, ndata); 784 | } 785 | 786 | int 787 | loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) 788 | { 789 | return _loadmemimage(i, r, data, ndata); 790 | } 791 | 792 | int 793 | unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) 794 | { 795 | return _unloadmemimage(i, r, data, ndata); 796 | } 797 | -------------------------------------------------------------------------------- /winsize.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "devdraw.h" 6 | 7 | int 8 | parsewinsize(char *s, Rectangle *r, int *havemin) 9 | { 10 | char c, *os; 11 | int i, j, k, l; 12 | 13 | os = s; 14 | *havemin = 0; 15 | *r = Rect(0,0,0,0); 16 | if(!isdigit((uchar)*s)) 17 | goto oops; 18 | i = strtol(s, &s, 0); 19 | if(*s == 'x'){ 20 | s++; 21 | if(!isdigit((uchar)*s)) 22 | goto oops; 23 | j = strtol(s, &s, 0); 24 | r->max.x = i; 25 | r->max.y = j; 26 | if(*s == 0) 27 | return 0; 28 | if(*s != '@') 29 | goto oops; 30 | 31 | s++; 32 | if(!isdigit((uchar)*s)) 33 | goto oops; 34 | i = strtol(s, &s, 0); 35 | if(*s != ',' && *s != ' ') 36 | goto oops; 37 | s++; 38 | if(!isdigit((uchar)*s)) 39 | goto oops; 40 | j = strtol(s, &s, 0); 41 | if(*s != 0) 42 | goto oops; 43 | *r = rectaddpt(*r, Pt(i,j)); 44 | *havemin = 1; 45 | return 0; 46 | } 47 | 48 | c = *s; 49 | if(c != ' ' && c != ',') 50 | goto oops; 51 | s++; 52 | if(!isdigit((uchar)*s)) 53 | goto oops; 54 | j = strtol(s, &s, 0); 55 | if(*s != c) 56 | goto oops; 57 | s++; 58 | if(!isdigit((uchar)*s)) 59 | goto oops; 60 | k = strtol(s, &s, 0); 61 | if(*s != c) 62 | goto oops; 63 | s++; 64 | if(!isdigit((uchar)*s)) 65 | goto oops; 66 | l = strtol(s, &s, 0); 67 | if(*s != 0) 68 | goto oops; 69 | *r = Rect(i,j,k,l); 70 | *havemin = 1; 71 | return 0; 72 | 73 | oops: 74 | werrstr("bad syntax in window size '%s'", os); 75 | return -1; 76 | } 77 | --------------------------------------------------------------------------------