1 #include "u.h" 2 #include "lib.h" 3 #include "dat.h" 4 #include "fns.h" 5 #include "error.h" 6 7 #define Image IMAGE 8 #include <draw.h> 9 #include <memdraw.h> 10 #include <memlayer.h> 11 #include <cursor.h> 12 #include "screen.h" 13 14 enum 15 { 16 Qtopdir = 0, 17 Qnew, 18 Q3rd, 19 Q2nd, 20 Qcolormap, 21 Qctl, 22 Qdata, 23 Qrefresh, 24 }; 25 26 /* 27 * Qid path is: 28 * 4 bits of file type (qids above) 29 * 24 bits of mux slot number +1; 0 means not attached to client 30 */ 31 #define QSHIFT 4 /* location in qid of client # */ 32 33 #define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) 34 #define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) 35 #define CLIENT(q) CLIENTPATH((q).path) 36 37 #define NHASH (1<<5) 38 #define HASHMASK (NHASH-1) 39 #define IOUNIT (64*1024) 40 41 typedef struct Client Client; 42 typedef struct Draw Draw; 43 typedef struct DImage DImage; 44 typedef struct DScreen DScreen; 45 typedef struct CScreen CScreen; 46 typedef struct FChar FChar; 47 typedef struct Refresh Refresh; 48 typedef struct Refx Refx; 49 typedef struct DName DName; 50 51 ulong blanktime = 30; /* in minutes; a half hour */ 52 53 struct Draw 54 { 55 QLock lk; 56 int clientid; 57 int nclient; 58 Client** client; 59 int nname; 60 DName* name; 61 int vers; 62 int softscreen; 63 int blanked; /* screen turned off */ 64 ulong blanktime; /* time of last operation */ 65 ulong savemap[3*256]; 66 }; 67 68 struct Client 69 { 70 Ref r; 71 DImage* dimage[NHASH]; 72 CScreen* cscreen; 73 Refresh* refresh; 74 Rendez refrend; 75 uchar* readdata; 76 int nreaddata; 77 int busy; 78 int clientid; 79 int slot; 80 int refreshme; 81 int infoid; 82 int op; 83 }; 84 85 struct Refresh 86 { 87 DImage* dimage; 88 Rectangle r; 89 Refresh* next; 90 }; 91 92 struct Refx 93 { 94 Client* client; 95 DImage* dimage; 96 }; 97 98 struct DName 99 { 100 char *name; 101 Client *client; 102 DImage* dimage; 103 int vers; 104 }; 105 106 struct FChar 107 { 108 int minx; /* left edge of bits */ 109 int maxx; /* right edge of bits */ 110 uchar miny; /* first non-zero scan-line */ 111 uchar maxy; /* last non-zero scan-line + 1 */ 112 schar left; /* offset of baseline */ 113 uchar width; /* width of baseline */ 114 }; 115 116 /* 117 * Reference counts in DImages: 118 * one per open by original client 119 * one per screen image or fill 120 * one per image derived from this one by name 121 */ 122 struct DImage 123 { 124 int id; 125 int ref; 126 char *name; 127 int vers; 128 Memimage* image; 129 int ascent; 130 int nfchar; 131 FChar* fchar; 132 DScreen* dscreen; /* 0 if not a window */ 133 DImage* fromname; /* image this one is derived from, by name */ 134 DImage* next; 135 }; 136 137 struct CScreen 138 { 139 DScreen* dscreen; 140 CScreen* next; 141 }; 142 143 struct DScreen 144 { 145 int id; 146 int public; 147 int ref; 148 DImage *dimage; 149 DImage *dfill; 150 Memscreen* screen; 151 Client* owner; 152 DScreen* next; 153 }; 154 155 static Draw sdraw; 156 static Memimage *screenimage; 157 static Memdata screendata; 158 static Rectangle flushrect; 159 static int waste; 160 static DScreen* dscreen; 161 extern void flushmemscreen(Rectangle); 162 void drawmesg(Client*, void*, int); 163 void drawuninstall(Client*, int); 164 void drawfreedimage(DImage*); 165 Client* drawclientofpath(ulong); 166 167 static char Enodrawimage[] = "unknown id for draw image"; 168 static char Enodrawscreen[] = "unknown id for draw screen"; 169 static char Eshortdraw[] = "short draw message"; 170 static char Eshortread[] = "draw read too short"; 171 static char Eimageexists[] = "image id in use"; 172 static char Escreenexists[] = "screen id in use"; 173 static char Edrawmem[] = "image memory allocation failed"; 174 static char Ereadoutside[] = "readimage outside image"; 175 static char Ewriteoutside[] = "writeimage outside image"; 176 static char Enotfont[] = "image not a font"; 177 static char Eindex[] = "character index out of range"; 178 static char Enoclient[] = "no such draw client"; 179 /* static char Edepth[] = "image has bad depth"; */ 180 static char Enameused[] = "image name in use"; 181 static char Enoname[] = "no image with that name"; 182 static char Eoldname[] = "named image no longer valid"; 183 static char Enamed[] = "image already has name"; 184 static char Ewrongname[] = "wrong name for image"; 185 186 int 187 drawcanqlock(void) 188 { 189 return canqlock(&sdraw.lk); 190 } 191 192 void 193 drawqlock(void) 194 { 195 qlock(&sdraw.lk); 196 } 197 198 void 199 drawqunlock(void) 200 { 201 qunlock(&sdraw.lk); 202 } 203 204 static int 205 drawgen(Chan *c, char *name, Dirtab *dt, int ndt, int s, Dir *dp) 206 { 207 int t; 208 Qid q; 209 ulong path; 210 Client *cl; 211 212 USED(name); 213 USED(dt); 214 USED(ndt); 215 216 q.vers = 0; 217 218 if(s == DEVDOTDOT){ 219 switch(QID(c->qid)){ 220 case Qtopdir: 221 case Q2nd: 222 mkqid(&q, Qtopdir, 0, QTDIR); 223 devdir(c, q, "#i", 0, eve, 0500, dp); 224 break; 225 case Q3rd: 226 cl = drawclientofpath(c->qid.path); 227 if(cl == nil) 228 strcpy(up->genbuf, "??"); 229 else 230 sprint(up->genbuf, "%d", cl->clientid); 231 mkqid(&q, Q2nd, 0, QTDIR); 232 devdir(c, q, up->genbuf, 0, eve, 0500, dp); 233 break; 234 default: 235 panic("drawwalk %llux", c->qid.path); 236 } 237 return 1; 238 } 239 240 /* 241 * Top level directory contains the name of the device. 242 */ 243 t = QID(c->qid); 244 if(t == Qtopdir){ 245 switch(s){ 246 case 0: 247 mkqid(&q, Q2nd, 0, QTDIR); 248 devdir(c, q, "draw", 0, eve, 0555, dp); 249 break; 250 default: 251 return -1; 252 } 253 return 1; 254 } 255 256 /* 257 * Second level contains "new" plus all the clients. 258 */ 259 if(t == Q2nd || t == Qnew){ 260 if(s == 0){ 261 mkqid(&q, Qnew, 0, QTFILE); 262 devdir(c, q, "new", 0, eve, 0666, dp); 263 } 264 else if(s <= sdraw.nclient){ 265 cl = sdraw.client[s-1]; 266 if(cl == 0) 267 return 0; 268 sprint(up->genbuf, "%d", cl->clientid); 269 mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR); 270 devdir(c, q, up->genbuf, 0, eve, 0555, dp); 271 return 1; 272 } 273 else 274 return -1; 275 return 1; 276 } 277 278 /* 279 * Third level. 280 */ 281 path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */ 282 q.vers = c->qid.vers; 283 q.type = QTFILE; 284 switch(s){ 285 case 0: 286 q.path = path|Qcolormap; 287 devdir(c, q, "colormap", 0, eve, 0600, dp); 288 break; 289 case 1: 290 q.path = path|Qctl; 291 devdir(c, q, "ctl", 0, eve, 0600, dp); 292 break; 293 case 2: 294 q.path = path|Qdata; 295 devdir(c, q, "data", 0, eve, 0600, dp); 296 break; 297 case 3: 298 q.path = path|Qrefresh; 299 devdir(c, q, "refresh", 0, eve, 0400, dp); 300 break; 301 default: 302 return -1; 303 } 304 return 1; 305 } 306 307 static 308 int 309 drawrefactive(void *a) 310 { 311 Client *c; 312 313 c = a; 314 return c->refreshme || c->refresh!=0; 315 } 316 317 static 318 void 319 drawrefreshscreen(DImage *l, Client *client) 320 { 321 while(l != nil && l->dscreen == nil) 322 l = l->fromname; 323 if(l != nil && l->dscreen->owner != client) 324 l->dscreen->owner->refreshme = 1; 325 } 326 327 static 328 void 329 drawrefresh(Memimage *m, Rectangle r, void *v) 330 { 331 Refx *x; 332 DImage *d; 333 Client *c; 334 Refresh *ref; 335 336 USED(m); 337 338 if(v == 0) 339 return; 340 x = v; 341 c = x->client; 342 d = x->dimage; 343 for(ref=c->refresh; ref; ref=ref->next) 344 if(ref->dimage == d){ 345 combinerect(&ref->r, r); 346 return; 347 } 348 ref = malloc(sizeof(Refresh)); 349 if(ref){ 350 ref->dimage = d; 351 ref->r = r; 352 ref->next = c->refresh; 353 c->refresh = ref; 354 } 355 } 356 357 static void 358 addflush(Rectangle r) 359 { 360 int abb, ar, anbb; 361 Rectangle nbb; 362 363 if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) 364 return; 365 366 if(flushrect.min.x >= flushrect.max.x){ 367 flushrect = r; 368 waste = 0; 369 return; 370 } 371 nbb = flushrect; 372 combinerect(&nbb, r); 373 ar = Dx(r)*Dy(r); 374 abb = Dx(flushrect)*Dy(flushrect); 375 anbb = Dx(nbb)*Dy(nbb); 376 /* 377 * Area of new waste is area of new bb minus area of old bb, 378 * less the area of the new segment, which we assume is not waste. 379 * This could be negative, but that's OK. 380 */ 381 waste += anbb-abb - ar; 382 if(waste < 0) 383 waste = 0; 384 /* 385 * absorb if: 386 * total area is small 387 * waste is less than half total area 388 * rectangles touch 389 */ 390 if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){ 391 flushrect = nbb; 392 return; 393 } 394 /* emit current state */ 395 if(flushrect.min.x < flushrect.max.x) 396 flushmemscreen(flushrect); 397 flushrect = r; 398 waste = 0; 399 } 400 401 static 402 void 403 dstflush(int dstid, Memimage *dst, Rectangle r) 404 { 405 Memlayer *l; 406 407 if(dstid == 0){ 408 combinerect(&flushrect, r); 409 return; 410 } 411 /* how can this happen? -rsc, dec 12 2002 */ 412 if(dst == 0){ 413 print("nil dstflush\n"); 414 return; 415 } 416 l = dst->layer; 417 if(l == nil) 418 return; 419 do{ 420 if(l->screen->image->data != screenimage->data) 421 return; 422 r = rectaddpt(r, l->delta); 423 l = l->screen->image->layer; 424 }while(l); 425 addflush(r); 426 } 427 428 void 429 drawflush(void) 430 { 431 if(flushrect.min.x < flushrect.max.x) 432 flushmemscreen(flushrect); 433 flushrect = Rect(10000, 10000, -10000, -10000); 434 } 435 436 void 437 drawflushr(Rectangle r) 438 { 439 qlock(&sdraw.lk); 440 flushmemscreen(r); 441 qunlock(&sdraw.lk); 442 } 443 444 static 445 int 446 drawcmp(char *a, char *b, int n) 447 { 448 if(strlen(a) != n) 449 return 1; 450 return memcmp(a, b, n); 451 } 452 453 DName* 454 drawlookupname(int n, char *str) 455 { 456 DName *name, *ename; 457 458 name = sdraw.name; 459 ename = &name[sdraw.nname]; 460 for(; name<ename; name++) 461 if(drawcmp(name->name, str, n) == 0) 462 return name; 463 return 0; 464 } 465 466 int 467 drawgoodname(DImage *d) 468 { 469 DName *n; 470 471 /* if window, validate the screen's own images */ 472 if(d->dscreen) 473 if(drawgoodname(d->dscreen->dimage) == 0 474 || drawgoodname(d->dscreen->dfill) == 0) 475 return 0; 476 if(d->name == nil) 477 return 1; 478 n = drawlookupname(strlen(d->name), d->name); 479 if(n==nil || n->vers!=d->vers) 480 return 0; 481 return 1; 482 } 483 484 DImage* 485 drawlookup(Client *client, int id, int checkname) 486 { 487 DImage *d; 488 489 d = client->dimage[id&HASHMASK]; 490 while(d){ 491 if(d->id == id){ 492 if(checkname && !drawgoodname(d)) 493 error(Eoldname); 494 return d; 495 } 496 d = d->next; 497 } 498 return 0; 499 } 500 501 DScreen* 502 drawlookupdscreen(int id) 503 { 504 DScreen *s; 505 506 s = dscreen; 507 while(s){ 508 if(s->id == id) 509 return s; 510 s = s->next; 511 } 512 return 0; 513 } 514 515 DScreen* 516 drawlookupscreen(Client *client, int id, CScreen **cs) 517 { 518 CScreen *s; 519 520 s = client->cscreen; 521 while(s){ 522 if(s->dscreen->id == id){ 523 *cs = s; 524 return s->dscreen; 525 } 526 s = s->next; 527 } 528 error(Enodrawscreen); 529 return 0; 530 } 531 532 Memimage* 533 drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) 534 { 535 DImage *d; 536 537 d = malloc(sizeof(DImage)); 538 if(d == 0) 539 return 0; 540 d->id = id; 541 d->ref = 1; 542 d->name = 0; 543 d->vers = 0; 544 d->image = i; 545 d->nfchar = 0; 546 d->fchar = 0; 547 d->fromname = 0; 548 d->dscreen = dscreen; 549 d->next = client->dimage[id&HASHMASK]; 550 client->dimage[id&HASHMASK] = d; 551 return i; 552 } 553 554 Memscreen* 555 drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) 556 { 557 Memscreen *s; 558 CScreen *c; 559 560 c = malloc(sizeof(CScreen)); 561 if(dimage && dimage->image && dimage->image->chan == 0) 562 panic("bad image %p in drawinstallscreen", dimage->image); 563 564 if(c == 0) 565 return 0; 566 if(d == 0){ 567 d = malloc(sizeof(DScreen)); 568 if(d == 0){ 569 free(c); 570 return 0; 571 } 572 s = malloc(sizeof(Memscreen)); 573 if(s == 0){ 574 free(c); 575 free(d); 576 return 0; 577 } 578 s->frontmost = 0; 579 s->rearmost = 0; 580 d->dimage = dimage; 581 if(dimage){ 582 s->image = dimage->image; 583 dimage->ref++; 584 } 585 d->dfill = dfill; 586 if(dfill){ 587 s->fill = dfill->image; 588 dfill->ref++; 589 } 590 d->ref = 0; 591 d->id = id; 592 d->screen = s; 593 d->public = public; 594 d->next = dscreen; 595 d->owner = client; 596 dscreen = d; 597 } 598 c->dscreen = d; 599 d->ref++; 600 c->next = client->cscreen; 601 client->cscreen = c; 602 return d->screen; 603 } 604 605 void 606 drawdelname(DName *name) 607 { 608 int i; 609 610 i = name-sdraw.name; 611 memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); 612 sdraw.nname--; 613 } 614 615 void 616 drawfreedscreen(DScreen *this) 617 { 618 DScreen *ds, *next; 619 620 this->ref--; 621 if(this->ref < 0) 622 print("negative ref in drawfreedscreen\n"); 623 if(this->ref > 0) 624 return; 625 ds = dscreen; 626 if(ds == this){ 627 dscreen = this->next; 628 goto Found; 629 } 630 while((next = ds->next)){ /* assign = */ 631 if(next == this){ 632 ds->next = this->next; 633 goto Found; 634 } 635 ds = next; 636 } 637 error(Enodrawimage); 638 639 Found: 640 if(this->dimage) 641 drawfreedimage(this->dimage); 642 if(this->dfill) 643 drawfreedimage(this->dfill); 644 free(this->screen); 645 free(this); 646 } 647 648 void 649 drawfreedimage(DImage *dimage) 650 { 651 int i; 652 Memimage *l; 653 DScreen *ds; 654 655 dimage->ref--; 656 if(dimage->ref < 0) 657 print("negative ref in drawfreedimage\n"); 658 if(dimage->ref > 0) 659 return; 660 661 /* any names? */ 662 for(i=0; i<sdraw.nname; ) 663 if(sdraw.name[i].dimage == dimage) 664 drawdelname(sdraw.name+i); 665 else 666 i++; 667 if(dimage->fromname){ /* acquired by name; owned by someone else*/ 668 drawfreedimage(dimage->fromname); 669 goto Return; 670 } 671 if(dimage->image == screenimage) /* don't free the display */ 672 goto Return; 673 ds = dimage->dscreen; 674 if(ds){ 675 l = dimage->image; 676 if(l->data == screenimage->data) 677 addflush(l->layer->screenr); 678 if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ 679 free(l->layer->refreshptr); 680 l->layer->refreshptr = nil; 681 if(drawgoodname(dimage)) 682 memldelete(l); 683 else 684 memlfree(l); 685 drawfreedscreen(ds); 686 }else 687 freememimage(dimage->image); 688 Return: 689 free(dimage->fchar); 690 free(dimage); 691 } 692 693 void 694 drawuninstallscreen(Client *client, CScreen *this) 695 { 696 CScreen *cs, *next; 697 698 cs = client->cscreen; 699 if(cs == this){ 700 client->cscreen = this->next; 701 drawfreedscreen(this->dscreen); 702 free(this); 703 return; 704 } 705 while((next = cs->next)){ /* assign = */ 706 if(next == this){ 707 cs->next = this->next; 708 drawfreedscreen(this->dscreen); 709 free(this); 710 return; 711 } 712 cs = next; 713 } 714 } 715 716 void 717 drawuninstall(Client *client, int id) 718 { 719 DImage *d, *next; 720 721 d = client->dimage[id&HASHMASK]; 722 if(d == 0) 723 error(Enodrawimage); 724 if(d->id == id){ 725 client->dimage[id&HASHMASK] = d->next; 726 drawfreedimage(d); 727 return; 728 } 729 while((next = d->next)){ /* assign = */ 730 if(next->id == id){ 731 d->next = next->next; 732 drawfreedimage(next); 733 return; 734 } 735 d = next; 736 } 737 error(Enodrawimage); 738 } 739 740 void 741 drawaddname(Client *client, DImage *di, int n, char *str) 742 { 743 DName *name, *ename, *new, *t; 744 745 name = sdraw.name; 746 ename = &name[sdraw.nname]; 747 for(; name<ename; name++) 748 if(drawcmp(name->name, str, n) == 0) 749 error(Enameused); 750 t = smalloc((sdraw.nname+1)*sizeof(DName)); 751 memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); 752 free(sdraw.name); 753 sdraw.name = t; 754 new = &sdraw.name[sdraw.nname++]; 755 new->name = smalloc(n+1); 756 memmove(new->name, str, n); 757 new->name[n] = 0; 758 new->dimage = di; 759 new->client = client; 760 new->vers = ++sdraw.vers; 761 } 762 763 Client* 764 drawnewclient(void) 765 { 766 Client *cl, **cp; 767 int i; 768 769 for(i=0; i<sdraw.nclient; i++){ 770 cl = sdraw.client[i]; 771 if(cl == 0) 772 break; 773 } 774 if(i == sdraw.nclient){ 775 cp = malloc((sdraw.nclient+1)*sizeof(Client*)); 776 if(cp == 0) 777 return 0; 778 memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*)); 779 free(sdraw.client); 780 sdraw.client = cp; 781 sdraw.nclient++; 782 cp[i] = 0; 783 } 784 cl = malloc(sizeof(Client)); 785 if(cl == 0) 786 return 0; 787 memset(cl, 0, sizeof(Client)); 788 cl->slot = i; 789 cl->clientid = ++sdraw.clientid; 790 cl->op = SoverD; 791 sdraw.client[i] = cl; 792 return cl; 793 } 794 795 static int 796 drawclientop(Client *cl) 797 { 798 int op; 799 800 op = cl->op; 801 cl->op = SoverD; 802 return op; 803 } 804 805 int 806 drawhasclients(void) 807 { 808 /* 809 * if draw has ever been used, we can't resize the frame buffer, 810 * even if all clients have exited (nclients is cumulative); it's too 811 * hard to make work. 812 */ 813 return sdraw.nclient != 0; 814 } 815 816 Client* 817 drawclientofpath(ulong path) 818 { 819 Client *cl; 820 int slot; 821 822 slot = CLIENTPATH(path); 823 if(slot == 0) 824 return nil; 825 cl = sdraw.client[slot-1]; 826 if(cl==0 || cl->clientid==0) 827 return nil; 828 return cl; 829 } 830 831 832 Client* 833 drawclient(Chan *c) 834 { 835 Client *client; 836 837 client = drawclientofpath(c->qid.path); 838 if(client == nil) 839 error(Enoclient); 840 return client; 841 } 842 843 Memimage* 844 drawimage(Client *client, uchar *a) 845 { 846 DImage *d; 847 848 d = drawlookup(client, BGLONG(a), 1); 849 if(d == nil) 850 error(Enodrawimage); 851 return d->image; 852 } 853 854 void 855 drawrectangle(Rectangle *r, uchar *a) 856 { 857 r->min.x = BGLONG(a+0*4); 858 r->min.y = BGLONG(a+1*4); 859 r->max.x = BGLONG(a+2*4); 860 r->max.y = BGLONG(a+3*4); 861 } 862 863 void 864 drawpoint(Point *p, uchar *a) 865 { 866 p->x = BGLONG(a+0*4); 867 p->y = BGLONG(a+1*4); 868 } 869 870 Point 871 drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) 872 { 873 FChar *fc; 874 Rectangle r; 875 Point sp1; 876 877 fc = &font->fchar[index]; 878 r.min.x = p.x+fc->left; 879 r.min.y = p.y-(font->ascent-fc->miny); 880 r.max.x = r.min.x+(fc->maxx-fc->minx); 881 r.max.y = r.min.y+(fc->maxy-fc->miny); 882 sp1.x = sp->x+fc->left; 883 sp1.y = sp->y+fc->miny; 884 memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); 885 p.x += fc->width; 886 sp->x += fc->width; 887 return p; 888 } 889 890 static int 891 initscreenimage(void) 892 { 893 int width, depth; 894 ulong chan; 895 void *X; 896 Rectangle r; 897 898 if(screenimage != nil) 899 return 1; 900 901 screendata.base = nil; 902 screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen, &X); 903 if(screendata.bdata == nil && X == nil) 904 return 0; 905 screendata.ref = 1; 906 907 screenimage = allocmemimaged(r, chan, &screendata, X); 908 if(screenimage == nil){ 909 /* RSC: BUG: detach screen */ 910 return 0; 911 } 912 913 screenimage->width = width; 914 screenimage->clipr = r; 915 return 1; 916 } 917 918 void 919 deletescreenimage(void) 920 { 921 qlock(&sdraw.lk); 922 /* RSC: BUG: detach screen */ 923 if(screenimage) 924 freememimage(screenimage); 925 screenimage = nil; 926 qunlock(&sdraw.lk); 927 } 928 929 static Chan* 930 drawattach(char *spec) 931 { 932 qlock(&sdraw.lk); 933 if(!initscreenimage()){ 934 qunlock(&sdraw.lk); 935 error("no frame buffer"); 936 } 937 qunlock(&sdraw.lk); 938 return devattach('i', spec); 939 } 940 941 static Walkqid* 942 drawwalk(Chan *c, Chan *nc, char **name, int nname) 943 { 944 if(screendata.bdata == nil) 945 error("no frame buffer"); 946 return devwalk(c, nc, name, nname, 0, 0, drawgen); 947 } 948 949 static int 950 drawstat(Chan *c, uchar *db, int n) 951 { 952 return devstat(c, db, n, 0, 0, drawgen); 953 } 954 955 static Chan* 956 drawopen(Chan *c, int omode) 957 { 958 Client *cl; 959 960 if(c->qid.type & QTDIR){ 961 c = devopen(c, omode, 0, 0, drawgen); 962 c->iounit = IOUNIT; 963 } 964 965 qlock(&sdraw.lk); 966 if(waserror()){ 967 qunlock(&sdraw.lk); 968 nexterror(); 969 } 970 971 if(QID(c->qid) == Qnew){ 972 cl = drawnewclient(); 973 if(cl == 0) 974 error(Enodev); 975 c->qid.path = Qctl|((cl->slot+1)<<QSHIFT); 976 } 977 978 switch(QID(c->qid)){ 979 case Qnew: 980 break; 981 982 case Qctl: 983 cl = drawclient(c); 984 if(cl->busy) 985 error(Einuse); 986 cl->busy = 1; 987 flushrect = Rect(10000, 10000, -10000, -10000); 988 drawinstall(cl, 0, screenimage, 0); 989 incref(&cl->r); 990 break; 991 case Qcolormap: 992 case Qdata: 993 case Qrefresh: 994 cl = drawclient(c); 995 incref(&cl->r); 996 break; 997 } 998 qunlock(&sdraw.lk); 999 poperror(); 1000 c->mode = openmode(omode); 1001 c->flag |= COPEN; 1002 c->offset = 0; 1003 c->iounit = IOUNIT; 1004 return c; 1005 } 1006 1007 static void 1008 drawclose(Chan *c) 1009 { 1010 int i; 1011 DImage *d, **dp; 1012 Client *cl; 1013 Refresh *r; 1014 1015 if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ 1016 return; 1017 qlock(&sdraw.lk); 1018 if(waserror()){ 1019 qunlock(&sdraw.lk); 1020 nexterror(); 1021 } 1022 1023 cl = drawclient(c); 1024 if(QID(c->qid) == Qctl) 1025 cl->busy = 0; 1026 if((c->flag&COPEN) && (decref(&cl->r)==0)){ 1027 while((r = cl->refresh)){ /* assign = */ 1028 cl->refresh = r->next; 1029 free(r); 1030 } 1031 /* free names */ 1032 for(i=0; i<sdraw.nname; ) 1033 if(sdraw.name[i].client == cl) 1034 drawdelname(sdraw.name+i); 1035 else 1036 i++; 1037 while(cl->cscreen) 1038 drawuninstallscreen(cl, cl->cscreen); 1039 /* all screens are freed, so now we can free images */ 1040 dp = cl->dimage; 1041 for(i=0; i<NHASH; i++){ 1042 while((d = *dp) != nil){ 1043 *dp = d->next; 1044 drawfreedimage(d); 1045 } 1046 dp++; 1047 } 1048 sdraw.client[cl->slot] = 0; 1049 drawflush(); /* to erase visible, now dead windows */ 1050 free(cl); 1051 } 1052 qunlock(&sdraw.lk); 1053 poperror(); 1054 } 1055 1056 long 1057 drawread(Chan *c, void *a, long n, vlong off) 1058 { 1059 int index, m; 1060 ulong red, green, blue; 1061 Client *cl; 1062 uchar *p; 1063 Refresh *r; 1064 DImage *di; 1065 Memimage *i; 1066 ulong offset = off; 1067 char buf[16]; 1068 1069 if(c->qid.type & QTDIR) 1070 return devdirread(c, a, n, 0, 0, drawgen); 1071 cl = drawclient(c); 1072 qlock(&sdraw.lk); 1073 if(waserror()){ 1074 qunlock(&sdraw.lk); 1075 nexterror(); 1076 } 1077 switch(QID(c->qid)){ 1078 case Qctl: 1079 if(n < 12*12) 1080 error(Eshortread); 1081 if(cl->infoid < 0) 1082 error(Enodrawimage); 1083 if(cl->infoid == 0){ 1084 i = screenimage; 1085 if(i == nil) 1086 error(Enodrawimage); 1087 }else{ 1088 di = drawlookup(cl, cl->infoid, 1); 1089 if(di == nil) 1090 error(Enodrawimage); 1091 i = di->image; 1092 } 1093 n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", 1094 cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, 1095 i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, 1096 i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); 1097 cl->infoid = -1; 1098 break; 1099 1100 case Qcolormap: 1101 drawactive(1); /* to restore map from backup */ 1102 p = malloc(4*12*256+1); 1103 if(p == 0) 1104 error(Enomem); 1105 m = 0; 1106 for(index = 0; index < 256; index++){ 1107 getcolor(index, &red, &green, &blue); 1108 m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); 1109 } 1110 n = readstr(offset, a, n, (char*)p); 1111 free(p); 1112 break; 1113 1114 case Qdata: 1115 if(cl->readdata == nil) 1116 error("no draw data"); 1117 if(n < cl->nreaddata) 1118 error(Eshortread); 1119 n = cl->nreaddata; 1120 memmove(a, cl->readdata, cl->nreaddata); 1121 free(cl->readdata); 1122 cl->readdata = nil; 1123 break; 1124 1125 case Qrefresh: 1126 if(n < 5*4) 1127 error(Ebadarg); 1128 for(;;){ 1129 if(cl->refreshme || cl->refresh) 1130 break; 1131 qunlock(&sdraw.lk); 1132 if(waserror()){ 1133 qlock(&sdraw.lk); /* restore lock for waserror() above */ 1134 nexterror(); 1135 } 1136 sleep(&cl->refrend, drawrefactive, cl); 1137 poperror(); 1138 qlock(&sdraw.lk); 1139 } 1140 p = a; 1141 while(cl->refresh && n>=5*4){ 1142 r = cl->refresh; 1143 BPLONG(p+0*4, r->dimage->id); 1144 BPLONG(p+1*4, r->r.min.x); 1145 BPLONG(p+2*4, r->r.min.y); 1146 BPLONG(p+3*4, r->r.max.x); 1147 BPLONG(p+4*4, r->r.max.y); 1148 cl->refresh = r->next; 1149 free(r); 1150 p += 5*4; 1151 n -= 5*4; 1152 } 1153 cl->refreshme = 0; 1154 n = p-(uchar*)a; 1155 } 1156 qunlock(&sdraw.lk); 1157 poperror(); 1158 return n; 1159 } 1160 1161 void 1162 drawwakeall(void) 1163 { 1164 Client *cl; 1165 int i; 1166 1167 for(i=0; i<sdraw.nclient; i++){ 1168 cl = sdraw.client[i]; 1169 if(cl && (cl->refreshme || cl->refresh)) 1170 wakeup(&cl->refrend); 1171 } 1172 } 1173 1174 static long 1175 drawwrite(Chan *c, void *a, long n, vlong offset) 1176 { 1177 char buf[128], *fields[4], *q; 1178 Client *cl; 1179 int i, m, red, green, blue, x; 1180 1181 USED(offset); 1182 1183 if(c->qid.type & QTDIR) 1184 error(Eisdir); 1185 cl = drawclient(c); 1186 qlock(&sdraw.lk); 1187 if(waserror()){ 1188 drawwakeall(); 1189 qunlock(&sdraw.lk); 1190 nexterror(); 1191 } 1192 switch(QID(c->qid)){ 1193 case Qctl: 1194 if(n != 4) 1195 error("unknown draw control request"); 1196 cl->infoid = BGLONG((uchar*)a); 1197 break; 1198 1199 case Qcolormap: 1200 drawactive(1); /* to restore map from backup */ 1201 m = n; 1202 n = 0; 1203 while(m > 0){ 1204 x = m; 1205 if(x > sizeof(buf)-1) 1206 x = sizeof(buf)-1; 1207 q = memccpy(buf, a, '\n', x); 1208 if(q == 0) 1209 break; 1210 i = q-buf; 1211 n += i; 1212 a = (char*)a + i; 1213 m -= i; 1214 *q = 0; 1215 if(tokenize(buf, fields, nelem(fields)) != 4) 1216 error(Ebadarg); 1217 i = strtoul(fields[0], 0, 0); 1218 red = strtoul(fields[1], 0, 0); 1219 green = strtoul(fields[2], 0, 0); 1220 blue = strtoul(fields[3], &q, 0); 1221 if(fields[3] == q) 1222 error(Ebadarg); 1223 if(red>255 || green>255 || blue>255 || i<0 || i>255) 1224 error(Ebadarg); 1225 red |= red<<8; 1226 red |= red<<16; 1227 green |= green<<8; 1228 green |= green<<16; 1229 blue |= blue<<8; 1230 blue |= blue<<16; 1231 setcolor(i, red, green, blue); 1232 } 1233 break; 1234 1235 case Qdata: 1236 drawmesg(cl, a, n); 1237 drawwakeall(); 1238 break; 1239 1240 default: 1241 error(Ebadusefd); 1242 } 1243 qunlock(&sdraw.lk); 1244 poperror(); 1245 return n; 1246 } 1247 1248 uchar* 1249 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) 1250 { 1251 int b, x; 1252 1253 if(p >= maxp) 1254 error(Eshortdraw); 1255 b = *p++; 1256 x = b & 0x7F; 1257 if(b & 0x80){ 1258 if(p+1 >= maxp) 1259 error(Eshortdraw); 1260 x |= *p++ << 7; 1261 x |= *p++ << 15; 1262 if(x & (1<<22)) 1263 x |= ~0<<23; 1264 }else{ 1265 if(b & 0x40) 1266 x |= ~0<<7; 1267 x += oldx; 1268 } 1269 *newx = x; 1270 return p; 1271 } 1272 1273 static void 1274 printmesg(char *fmt, uchar *a, int plsprnt) 1275 { 1276 char buf[256]; 1277 char *p, *q; 1278 int s; 1279 1280 if(1|| plsprnt==0){ 1281 SET(s); 1282 SET(q); 1283 SET(p); 1284 USED(fmt); 1285 USED(a); 1286 USED(buf); 1287 USED(p); 1288 USED(q); 1289 USED(s); 1290 return; 1291 } 1292 q = buf; 1293 *q++ = *a++; 1294 for(p=fmt; *p; p++){ 1295 switch(*p){ 1296 case 'l': 1297 q += sprint(q, " %ld", (long)BGLONG(a)); 1298 a += 4; 1299 break; 1300 case 'L': 1301 q += sprint(q, " %.8lux", (ulong)BGLONG(a)); 1302 a += 4; 1303 break; 1304 case 'R': 1305 q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); 1306 a += 16; 1307 break; 1308 case 'P': 1309 q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); 1310 a += 8; 1311 break; 1312 case 'b': 1313 q += sprint(q, " %d", *a++); 1314 break; 1315 case 's': 1316 q += sprint(q, " %d", BGSHORT(a)); 1317 a += 2; 1318 break; 1319 case 'S': 1320 q += sprint(q, " %.4ux", BGSHORT(a)); 1321 a += 2; 1322 break; 1323 } 1324 } 1325 *q++ = '\n'; 1326 *q = 0; 1327 iprint("%.*s", (int)(q-buf), buf); 1328 } 1329 1330 void 1331 drawmesg(Client *client, void *av, int n) 1332 { 1333 int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush; 1334 uchar *u, *a, refresh; 1335 char *fmt; 1336 ulong value, chan; 1337 Rectangle r, clipr; 1338 Point p, q, *pp, sp; 1339 Memimage *i, *dst, *src, *mask; 1340 Memimage *l, **lp; 1341 Memscreen *scrn; 1342 DImage *font, *ll, *di, *ddst, *dsrc; 1343 DName *dn; 1344 DScreen *dscrn; 1345 FChar *fc; 1346 Refx *refx; 1347 CScreen *cs; 1348 Refreshfn reffn; 1349 1350 a = av; 1351 m = 0; 1352 fmt = nil; 1353 if(waserror()){ 1354 if(fmt) printmesg(fmt, a, 1); 1355 /* iprint("error: %s\n", up->errstr); */ 1356 nexterror(); 1357 } 1358 while((n-=m) > 0){ 1359 USED(fmt); 1360 a += m; 1361 switch(*a){ 1362 default: 1363 error("bad draw command"); 1364 /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ 1365 case 'b': 1366 printmesg(fmt="LLbLbRRL", a, 0); 1367 m = 1+4+4+1+4+1+4*4+4*4+4; 1368 if(n < m) 1369 error(Eshortdraw); 1370 dstid = BGLONG(a+1); 1371 scrnid = BGSHORT(a+5); 1372 refresh = a[9]; 1373 chan = BGLONG(a+10); 1374 repl = a[14]; 1375 drawrectangle(&r, a+15); 1376 drawrectangle(&clipr, a+31); 1377 value = BGLONG(a+47); 1378 if(drawlookup(client, dstid, 0)) 1379 error(Eimageexists); 1380 if(scrnid){ 1381 dscrn = drawlookupscreen(client, scrnid, &cs); 1382 scrn = dscrn->screen; 1383 if(repl || chan!=scrn->image->chan) 1384 error("image parameters incompatible with screen"); 1385 reffn = nil; 1386 switch(refresh){ 1387 case Refbackup: 1388 break; 1389 case Refnone: 1390 reffn = memlnorefresh; 1391 break; 1392 case Refmesg: 1393 reffn = drawrefresh; 1394 break; 1395 default: 1396 error("unknown refresh method"); 1397 } 1398 l = memlalloc(scrn, r, reffn, 0, value); 1399 if(l == 0) 1400 error(Edrawmem); 1401 addflush(l->layer->screenr); 1402 l->clipr = clipr; 1403 rectclip(&l->clipr, r); 1404 if(drawinstall(client, dstid, l, dscrn) == 0){ 1405 memldelete(l); 1406 error(Edrawmem); 1407 } 1408 dscrn->ref++; 1409 if(reffn){ 1410 refx = nil; 1411 if(reffn == drawrefresh){ 1412 refx = malloc(sizeof(Refx)); 1413 if(refx == 0){ 1414 drawuninstall(client, dstid); 1415 error(Edrawmem); 1416 } 1417 refx->client = client; 1418 refx->dimage = drawlookup(client, dstid, 1); 1419 } 1420 memlsetrefresh(l, reffn, refx); 1421 } 1422 continue; 1423 } 1424 i = allocmemimage(r, chan); 1425 if(i == 0) 1426 error(Edrawmem); 1427 if(repl) 1428 i->flags |= Frepl; 1429 i->clipr = clipr; 1430 if(!repl) 1431 rectclip(&i->clipr, r); 1432 if(drawinstall(client, dstid, i, 0) == 0){ 1433 freememimage(i); 1434 error(Edrawmem); 1435 } 1436 memfillcolor(i, value); 1437 continue; 1438 1439 /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ 1440 case 'A': 1441 printmesg(fmt="LLLb", a, 1); 1442 m = 1+4+4+4+1; 1443 if(n < m) 1444 error(Eshortdraw); 1445 dstid = BGLONG(a+1); 1446 if(dstid == 0) 1447 error(Ebadarg); 1448 if(drawlookupdscreen(dstid)) 1449 error(Escreenexists); 1450 ddst = drawlookup(client, BGLONG(a+5), 1); 1451 dsrc = drawlookup(client, BGLONG(a+9), 1); 1452 if(ddst==0 || dsrc==0) 1453 error(Enodrawimage); 1454 if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) 1455 error(Edrawmem); 1456 continue; 1457 1458 /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ 1459 case 'c': 1460 printmesg(fmt="LbR", a, 0); 1461 m = 1+4+1+4*4; 1462 if(n < m) 1463 error(Eshortdraw); 1464 ddst = drawlookup(client, BGLONG(a+1), 1); 1465 if(ddst == nil) 1466 error(Enodrawimage); 1467 if(ddst->name) 1468 error("can't change repl/clipr of shared image"); 1469 dst = ddst->image; 1470 if(a[5]) 1471 dst->flags |= Frepl; 1472 drawrectangle(&dst->clipr, a+6); 1473 continue; 1474 1475 /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ 1476 case 'd': 1477 printmesg(fmt="LLLRPP", a, 0); 1478 m = 1+4+4+4+4*4+2*4+2*4; 1479 if(n < m) 1480 error(Eshortdraw); 1481 dst = drawimage(client, a+1); 1482 dstid = BGLONG(a+1); 1483 src = drawimage(client, a+5); 1484 mask = drawimage(client, a+9); 1485 drawrectangle(&r, a+13); 1486 drawpoint(&p, a+29); 1487 drawpoint(&q, a+37); 1488 op = drawclientop(client); 1489 memdraw(dst, r, src, p, mask, q, op); 1490 dstflush(dstid, dst, r); 1491 continue; 1492 1493 /* toggle debugging: 'D' val[1] */ 1494 case 'D': 1495 printmesg(fmt="b", a, 0); 1496 m = 1+1; 1497 if(n < m) 1498 error(Eshortdraw); 1499 drawdebug = a[1]; 1500 continue; 1501 1502 /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ 1503 case 'e': 1504 case 'E': 1505 printmesg(fmt="LLPlllPll", a, 0); 1506 m = 1+4+4+2*4+4+4+4+2*4+2*4; 1507 if(n < m) 1508 error(Eshortdraw); 1509 dst = drawimage(client, a+1); 1510 dstid = BGLONG(a+1); 1511 src = drawimage(client, a+5); 1512 drawpoint(&p, a+9); 1513 e0 = BGLONG(a+17); 1514 e1 = BGLONG(a+21); 1515 if(e0<0 || e1<0) 1516 error("invalid ellipse semidiameter"); 1517 j = BGLONG(a+25); 1518 if(j < 0) 1519 error("negative ellipse thickness"); 1520 drawpoint(&sp, a+29); 1521 c = j; 1522 if(*a == 'E') 1523 c = -1; 1524 ox = BGLONG(a+37); 1525 oy = BGLONG(a+41); 1526 op = drawclientop(client); 1527 /* high bit indicates arc angles are present */ 1528 if(ox & (1<<31)){ 1529 if((ox & (1<<30)) == 0) 1530 ox &= ~(1<<31); 1531 memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); 1532 }else 1533 memellipse(dst, p, e0, e1, c, src, sp, op); 1534 dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); 1535 continue; 1536 1537 /* free: 'f' id[4] */ 1538 case 'f': 1539 printmesg(fmt="L", a, 1); 1540 m = 1+4; 1541 if(n < m) 1542 error(Eshortdraw); 1543 ll = drawlookup(client, BGLONG(a+1), 0); 1544 if(ll && ll->dscreen && ll->dscreen->owner != client) 1545 ll->dscreen->owner->refreshme = 1; 1546 drawuninstall(client, BGLONG(a+1)); 1547 continue; 1548 1549 /* free screen: 'F' id[4] */ 1550 case 'F': 1551 printmesg(fmt="L", a, 1); 1552 m = 1+4; 1553 if(n < m) 1554 error(Eshortdraw); 1555 drawlookupscreen(client, BGLONG(a+1), &cs); 1556 drawuninstallscreen(client, cs); 1557 continue; 1558 1559 /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ 1560 case 'i': 1561 printmesg(fmt="Llb", a, 1); 1562 m = 1+4+4+1; 1563 if(n < m) 1564 error(Eshortdraw); 1565 dstid = BGLONG(a+1); 1566 if(dstid == 0) 1567 error("can't use display as font"); 1568 font = drawlookup(client, dstid, 1); 1569 if(font == 0) 1570 error(Enodrawimage); 1571 if(font->image->layer) 1572 error("can't use window as font"); 1573 ni = BGLONG(a+5); 1574 if(ni<=0 || ni>4096) 1575 error("bad font size (4096 chars max)"); 1576 free(font->fchar); /* should we complain if non-zero? */ 1577 font->fchar = malloc(ni*sizeof(FChar)); 1578 if(font->fchar == 0) 1579 error("no memory for font"); 1580 memset(font->fchar, 0, ni*sizeof(FChar)); 1581 font->nfchar = ni; 1582 font->ascent = a[9]; 1583 continue; 1584 1585 /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ 1586 case 'l': 1587 printmesg(fmt="LLSRPbb", a, 0); 1588 m = 1+4+4+2+4*4+2*4+1+1; 1589 if(n < m) 1590 error(Eshortdraw); 1591 font = drawlookup(client, BGLONG(a+1), 1); 1592 if(font == 0) 1593 error(Enodrawimage); 1594 if(font->nfchar == 0) 1595 error(Enotfont); 1596 src = drawimage(client, a+5); 1597 ci = BGSHORT(a+9); 1598 if(ci >= font->nfchar) 1599 error(Eindex); 1600 drawrectangle(&r, a+11); 1601 drawpoint(&p, a+27); 1602 memdraw(font->image, r, src, p, memopaque, p, S); 1603 fc = &font->fchar[ci]; 1604 fc->minx = r.min.x; 1605 fc->maxx = r.max.x; 1606 fc->miny = r.min.y; 1607 fc->maxy = r.max.y; 1608 fc->left = a[35]; 1609 fc->width = a[36]; 1610 continue; 1611 1612 /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ 1613 case 'L': 1614 printmesg(fmt="LPPlllLP", a, 0); 1615 m = 1+4+2*4+2*4+4+4+4+4+2*4; 1616 if(n < m) 1617 error(Eshortdraw); 1618 dst = drawimage(client, a+1); 1619 dstid = BGLONG(a+1); 1620 drawpoint(&p, a+5); 1621 drawpoint(&q, a+13); 1622 e0 = BGLONG(a+21); 1623 e1 = BGLONG(a+25); 1624 j = BGLONG(a+29); 1625 if(j < 0) 1626 error("negative line width"); 1627 src = drawimage(client, a+33); 1628 drawpoint(&sp, a+37); 1629 op = drawclientop(client); 1630 memline(dst, p, q, e0, e1, j, src, sp, op); 1631 /* avoid memlinebbox if possible */ 1632 if(dstid==0 || dst->layer!=nil){ 1633 /* BUG: this is terribly inefficient: update maximal containing rect*/ 1634 r = memlinebbox(p, q, e0, e1, j); 1635 dstflush(dstid, dst, insetrect(r, -(1+1+j))); 1636 } 1637 continue; 1638 1639 /* create image mask: 'm' newid[4] id[4] */ 1640 /* 1641 * 1642 case 'm': 1643 printmesg("LL", a, 0); 1644 m = 4+4; 1645 if(n < m) 1646 error(Eshortdraw); 1647 break; 1648 * 1649 */ 1650 1651 /* attach to a named image: 'n' dstid[4] j[1] name[j] */ 1652 case 'n': 1653 printmesg(fmt="Lz", a, 0); 1654 m = 1+4+1; 1655 if(n < m) 1656 error(Eshortdraw); 1657 j = a[5]; 1658 if(j == 0) /* give me a non-empty name please */ 1659 error(Eshortdraw); 1660 m += j; 1661 if(n < m) 1662 error(Eshortdraw); 1663 dstid = BGLONG(a+1); 1664 if(drawlookup(client, dstid, 0)) 1665 error(Eimageexists); 1666 dn = drawlookupname(j, (char*)a+6); 1667 if(dn == nil) 1668 error(Enoname); 1669 if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) 1670 error(Edrawmem); 1671 di = drawlookup(client, dstid, 0); 1672 if(di == 0) 1673 error("draw: can't happen"); 1674 di->vers = dn->vers; 1675 di->name = smalloc(j+1); 1676 di->fromname = dn->dimage; 1677 di->fromname->ref++; 1678 memmove(di->name, a+6, j); 1679 di->name[j] = 0; 1680 client->infoid = dstid; 1681 continue; 1682 1683 /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ 1684 case 'N': 1685 printmesg(fmt="Lbz", a, 0); 1686 m = 1+4+1+1; 1687 if(n < m) 1688 error(Eshortdraw); 1689 c = a[5]; 1690 j = a[6]; 1691 if(j == 0) /* give me a non-empty name please */ 1692 error(Eshortdraw); 1693 m += j; 1694 if(n < m) 1695 error(Eshortdraw); 1696 di = drawlookup(client, BGLONG(a+1), 0); 1697 if(di == 0) 1698 error(Enodrawimage); 1699 if(di->name) 1700 error(Enamed); 1701 if(c) 1702 drawaddname(client, di, j, (char*)a+7); 1703 else{ 1704 dn = drawlookupname(j, (char*)a+7); 1705 if(dn == nil) 1706 error(Enoname); 1707 if(dn->dimage != di) 1708 error(Ewrongname); 1709 drawdelname(dn); 1710 } 1711 continue; 1712 1713 /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ 1714 case 'o': 1715 printmesg(fmt="LPP", a, 0); 1716 m = 1+4+2*4+2*4; 1717 if(n < m) 1718 error(Eshortdraw); 1719 dst = drawimage(client, a+1); 1720 if(dst->layer){ 1721 drawpoint(&p, a+5); 1722 drawpoint(&q, a+13); 1723 r = dst->layer->screenr; 1724 ni = memlorigin(dst, p, q); 1725 if(ni < 0) 1726 error("image origin failed"); 1727 if(ni > 0){ 1728 addflush(r); 1729 addflush(dst->layer->screenr); 1730 ll = drawlookup(client, BGLONG(a+1), 1); 1731 drawrefreshscreen(ll, client); 1732 } 1733 } 1734 continue; 1735 1736 /* set compositing operator for next draw operation: 'O' op */ 1737 case 'O': 1738 printmesg(fmt="b", a, 0); 1739 m = 1+1; 1740 if(n < m) 1741 error(Eshortdraw); 1742 client->op = a[1]; 1743 continue; 1744 1745 /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1746 /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1747 case 'p': 1748 case 'P': 1749 printmesg(fmt="LslllLPP", a, 0); 1750 m = 1+4+2+4+4+4+4+2*4; 1751 if(n < m) 1752 error(Eshortdraw); 1753 dstid = BGLONG(a+1); 1754 dst = drawimage(client, a+1); 1755 ni = BGSHORT(a+5); 1756 if(ni < 0) 1757 error("negative count in polygon"); 1758 e0 = BGLONG(a+7); 1759 e1 = BGLONG(a+11); 1760 j = 0; 1761 if(*a == 'p'){ 1762 j = BGLONG(a+15); 1763 if(j < 0) 1764 error("negative polygon line width"); 1765 } 1766 src = drawimage(client, a+19); 1767 drawpoint(&sp, a+23); 1768 drawpoint(&p, a+31); 1769 ni++; 1770 pp = malloc(ni*sizeof(Point)); 1771 if(pp == nil) 1772 error(Enomem); 1773 doflush = 0; 1774 if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) 1775 doflush = 1; /* simplify test in loop */ 1776 ox = oy = 0; 1777 esize = 0; 1778 u = a+m; 1779 for(y=0; y<ni; y++){ 1780 q = p; 1781 oesize = esize; 1782 u = drawcoord(u, a+n, ox, &p.x); 1783 u = drawcoord(u, a+n, oy, &p.y); 1784 ox = p.x; 1785 oy = p.y; 1786 if(doflush){ 1787 esize = j; 1788 if(*a == 'p'){ 1789 if(y == 0){ 1790 c = memlineendsize(e0); 1791 if(c > esize) 1792 esize = c; 1793 } 1794 if(y == ni-1){ 1795 c = memlineendsize(e1); 1796 if(c > esize) 1797 esize = c; 1798 } 1799 } 1800 if(*a=='P' && e0!=1 && e0 !=~0) 1801 r = dst->clipr; 1802 else if(y > 0){ 1803 r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); 1804 combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1805 } 1806 if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ 1807 dstflush(dstid, dst, r); 1808 } 1809 pp[y] = p; 1810 } 1811 if(y == 1) 1812 dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1813 op = drawclientop(client); 1814 if(*a == 'p') 1815 mempoly(dst, pp, ni, e0, e1, j, src, sp, op); 1816 else 1817 memfillpoly(dst, pp, ni, e0, src, sp, op); 1818 free(pp); 1819 m = u-a; 1820 continue; 1821 1822 /* read: 'r' id[4] R[4*4] */ 1823 case 'r': 1824 printmesg(fmt="LR", a, 0); 1825 m = 1+4+4*4; 1826 if(n < m) 1827 error(Eshortdraw); 1828 i = drawimage(client, a+1); 1829 drawrectangle(&r, a+5); 1830 if(!rectinrect(r, i->r)) 1831 error(Ereadoutside); 1832 c = bytesperline(r, i->depth); 1833 c *= Dy(r); 1834 free(client->readdata); 1835 client->readdata = mallocz(c, 0); 1836 if(client->readdata == nil) 1837 error("readimage malloc failed"); 1838 client->nreaddata = memunload(i, r, client->readdata, c); 1839 if(client->nreaddata < 0){ 1840 free(client->readdata); 1841 client->readdata = nil; 1842 error("bad readimage call"); 1843 } 1844 continue; 1845 1846 /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ 1847 /* 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]) */ 1848 case 's': 1849 case 'x': 1850 printmesg(fmt="LLLPRPs", a, 0); 1851 m = 1+4+4+4+2*4+4*4+2*4+2; 1852 if(*a == 'x') 1853 m += 4+2*4; 1854 if(n < m) 1855 error(Eshortdraw); 1856 1857 dst = drawimage(client, a+1); 1858 dstid = BGLONG(a+1); 1859 src = drawimage(client, a+5); 1860 font = drawlookup(client, BGLONG(a+9), 1); 1861 if(font == 0) 1862 error(Enodrawimage); 1863 if(font->nfchar == 0) 1864 error(Enotfont); 1865 drawpoint(&p, a+13); 1866 drawrectangle(&r, a+21); 1867 drawpoint(&sp, a+37); 1868 ni = BGSHORT(a+45); 1869 u = a+m; 1870 m += ni*2; 1871 if(n < m) 1872 error(Eshortdraw); 1873 clipr = dst->clipr; 1874 dst->clipr = r; 1875 op = drawclientop(client); 1876 if(*a == 'x'){ 1877 /* paint background */ 1878 l = drawimage(client, a+47); 1879 drawpoint(&q, a+51); 1880 r.min.x = p.x; 1881 r.min.y = p.y-font->ascent; 1882 r.max.x = p.x; 1883 r.max.y = r.min.y+Dy(font->image->r); 1884 j = ni; 1885 while(--j >= 0){ 1886 ci = BGSHORT(u); 1887 if(ci<0 || ci>=font->nfchar){ 1888 dst->clipr = clipr; 1889 error(Eindex); 1890 } 1891 r.max.x += font->fchar[ci].width; 1892 u += 2; 1893 } 1894 memdraw(dst, r, l, q, memopaque, ZP, op); 1895 u -= 2*ni; 1896 } 1897 q = p; 1898 while(--ni >= 0){ 1899 ci = BGSHORT(u); 1900 if(ci<0 || ci>=font->nfchar){ 1901 dst->clipr = clipr; 1902 error(Eindex); 1903 } 1904 q = drawchar(dst, q, src, &sp, font, ci, op); 1905 u += 2; 1906 } 1907 dst->clipr = clipr; 1908 p.y -= font->ascent; 1909 dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); 1910 continue; 1911 1912 /* use public screen: 'S' id[4] chan[4] */ 1913 case 'S': 1914 printmesg(fmt="Ll", a, 0); 1915 m = 1+4+4; 1916 if(n < m) 1917 error(Eshortdraw); 1918 dstid = BGLONG(a+1); 1919 if(dstid == 0) 1920 error(Ebadarg); 1921 dscrn = drawlookupdscreen(dstid); 1922 if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) 1923 error(Enodrawscreen); 1924 if(dscrn->screen->image->chan != BGLONG(a+5)) 1925 error("inconsistent chan"); 1926 if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) 1927 error(Edrawmem); 1928 continue; 1929 1930 /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ 1931 case 't': 1932 printmesg(fmt="bsL", a, 0); 1933 m = 1+1+2; 1934 if(n < m) 1935 error(Eshortdraw); 1936 nw = BGSHORT(a+2); 1937 if(nw < 0) 1938 error(Ebadarg); 1939 if(nw == 0) 1940 continue; 1941 m += nw*4; 1942 if(n < m) 1943 error(Eshortdraw); 1944 lp = malloc(nw*sizeof(Memimage*)); 1945 if(lp == 0) 1946 error(Enomem); 1947 if(waserror()){ 1948 free(lp); 1949 nexterror(); 1950 } 1951 for(j=0; j<nw; j++) 1952 lp[j] = drawimage(client, a+1+1+2+j*4); 1953 if(lp[0]->layer == 0) 1954 error("images are not windows"); 1955 for(j=1; j<nw; j++) 1956 if(lp[j]->layer->screen != lp[0]->layer->screen) 1957 error("images not on same screen"); 1958 if(a[1]) 1959 memltofrontn(lp, nw); 1960 else 1961 memltorearn(lp, nw); 1962 if(lp[0]->layer->screen->image->data == screenimage->data) 1963 for(j=0; j<nw; j++) 1964 addflush(lp[j]->layer->screenr); 1965 ll = drawlookup(client, BGLONG(a+1+1+2), 1); 1966 drawrefreshscreen(ll, client); 1967 poperror(); 1968 free(lp); 1969 continue; 1970 1971 /* visible: 'v' */ 1972 case 'v': 1973 printmesg(fmt="", a, 0); 1974 m = 1; 1975 drawflush(); 1976 continue; 1977 1978 /* write: 'y' id[4] R[4*4] data[x*1] */ 1979 /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ 1980 case 'y': 1981 case 'Y': 1982 printmesg(fmt="LR", a, 0); 1983 // iprint("load %c\n", *a); 1984 m = 1+4+4*4; 1985 if(n < m) 1986 error(Eshortdraw); 1987 dstid = BGLONG(a+1); 1988 dst = drawimage(client, a+1); 1989 drawrectangle(&r, a+5); 1990 if(!rectinrect(r, dst->r)) 1991 error(Ewriteoutside); 1992 y = memload(dst, r, a+m, n-m, *a=='Y'); 1993 if(y < 0) 1994 error("bad writeimage call"); 1995 dstflush(dstid, dst, r); 1996 m += y; 1997 continue; 1998 } 1999 } 2000 poperror(); 2001 } 2002 2003 Dev drawdevtab = { 2004 'i', 2005 "draw", 2006 2007 devreset, 2008 devinit, 2009 devshutdown, 2010 drawattach, 2011 drawwalk, 2012 drawstat, 2013 drawopen, 2014 devcreate, 2015 drawclose, 2016 drawread, 2017 devbread, 2018 drawwrite, 2019 devbwrite, 2020 devremove, 2021 devwstat, 2022 }; 2023 2024 /* 2025 * On 8 bit displays, load the default color map 2026 */ 2027 void 2028 drawcmap(void) 2029 { 2030 int r, g, b, cr, cg, cb, v; 2031 int num, den; 2032 int i, j; 2033 2034 drawactive(1); /* to restore map from backup */ 2035 for(r=0,i=0; r!=4; r++) 2036 for(v=0; v!=4; v++,i+=16){ 2037 for(g=0,j=v-r; g!=4; g++) 2038 for(b=0;b!=4;b++,j++){ 2039 den = r; 2040 if(g > den) 2041 den = g; 2042 if(b > den) 2043 den = b; 2044 if(den == 0) /* divide check -- pick grey shades */ 2045 cr = cg = cb = v*17; 2046 else{ 2047 num = 17*(4*den+v); 2048 cr = r*num/den; 2049 cg = g*num/den; 2050 cb = b*num/den; 2051 } 2052 setcolor(i+(j&15), 2053 cr*0x01010101, cg*0x01010101, cb*0x01010101); 2054 } 2055 } 2056 } 2057 2058 void 2059 drawblankscreen(int blank) 2060 { 2061 int i, nc; 2062 ulong *p; 2063 2064 if(blank == sdraw.blanked) 2065 return; 2066 if(!canqlock(&sdraw.lk)) 2067 return; 2068 if(!initscreenimage()){ 2069 qunlock(&sdraw.lk); 2070 return; 2071 } 2072 p = sdraw.savemap; 2073 nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth; 2074 2075 /* 2076 * blankscreen uses the hardware to blank the screen 2077 * when possible. to help in cases when it is not possible, 2078 * we set the color map to be all black. 2079 */ 2080 if(blank == 0){ /* turn screen on */ 2081 for(i=0; i<nc; i++, p+=3) 2082 setcolor(i, p[0], p[1], p[2]); 2083 // blankscreen(0); 2084 }else{ /* turn screen off */ 2085 // blankscreen(1); 2086 for(i=0; i<nc; i++, p+=3){ 2087 getcolor(i, &p[0], &p[1], &p[2]); 2088 setcolor(i, 0, 0, 0); 2089 } 2090 } 2091 sdraw.blanked = blank; 2092 qunlock(&sdraw.lk); 2093 } 2094 2095 /* 2096 * record activity on screen, changing blanking as appropriate 2097 */ 2098 void 2099 drawactive(int active) 2100 { 2101 /* 2102 if(active){ 2103 drawblankscreen(0); 2104 sdraw.blanktime = MACHP(0)->ticks; 2105 }else{ 2106 if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime) 2107 drawblankscreen(1); 2108 } 2109 */ 2110 } 2111 2112 int 2113 drawidletime(void) 2114 { 2115 return 0; 2116 /* return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; */ 2117 } 2118 2119