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 #define isvgascreen(dst) 1 871 872 873 Point 874 drawchar(Memimage *dst, Memimage *rdst, Point p, 875 Memimage *src, Point *sp, DImage *font, int index, int op) 876 { 877 FChar *fc; 878 Rectangle r; 879 Point sp1; 880 static Memimage *tmp; 881 882 fc = &font->fchar[index]; 883 r.min.x = p.x+fc->left; 884 r.min.y = p.y-(font->ascent-fc->miny); 885 r.max.x = r.min.x+(fc->maxx-fc->minx); 886 r.max.y = r.min.y+(fc->maxy-fc->miny); 887 sp1.x = sp->x+fc->left; 888 sp1.y = sp->y+fc->miny; 889 890 /* 891 * If we're drawing greyscale fonts onto a VGA screen, 892 * it's very costly to read the screen memory to do the 893 * alpha blending inside memdraw. If this is really a stringbg, 894 * then rdst is the bg image (in main memory) which we can 895 * refer to for the underlying dst pixels instead of reading dst 896 * directly. 897 */ 898 if(1 || (isvgascreen(dst) && !isvgascreen(rdst) /*&& font->image->depth > 1*/)){ 899 if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){ 900 if(tmp) 901 freememimage(tmp); 902 tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan); 903 if(tmp == nil) 904 goto fallback; 905 } 906 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S); 907 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op); 908 memdraw(dst, r, tmp, ZP, memopaque, ZP, S); 909 }else{ 910 fallback: 911 memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); 912 } 913 914 p.x += fc->width; 915 sp->x += fc->width; 916 return p; 917 } 918 919 static int 920 initscreenimage(void) 921 { 922 int width, depth; 923 ulong chan; 924 void *X; 925 Rectangle r; 926 927 if(screenimage != nil) 928 return 1; 929 930 screendata.base = nil; 931 screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen, &X); 932 if(screendata.bdata == nil && X == nil) 933 return 0; 934 screendata.ref = 1; 935 936 screenimage = allocmemimaged(r, chan, &screendata, X); 937 if(screenimage == nil){ 938 /* RSC: BUG: detach screen */ 939 return 0; 940 } 941 942 screenimage->width = width; 943 screenimage->clipr = r; 944 return 1; 945 } 946 947 void 948 deletescreenimage(void) 949 { 950 qlock(&sdraw.lk); 951 /* RSC: BUG: detach screen */ 952 if(screenimage) 953 freememimage(screenimage); 954 screenimage = nil; 955 qunlock(&sdraw.lk); 956 } 957 958 static Chan* 959 drawattach(char *spec) 960 { 961 qlock(&sdraw.lk); 962 if(!initscreenimage()){ 963 qunlock(&sdraw.lk); 964 error("no frame buffer"); 965 } 966 qunlock(&sdraw.lk); 967 return devattach('i', spec); 968 } 969 970 static Walkqid* 971 drawwalk(Chan *c, Chan *nc, char **name, int nname) 972 { 973 if(screendata.bdata == nil) 974 error("no frame buffer"); 975 return devwalk(c, nc, name, nname, 0, 0, drawgen); 976 } 977 978 static int 979 drawstat(Chan *c, uchar *db, int n) 980 { 981 return devstat(c, db, n, 0, 0, drawgen); 982 } 983 984 static Chan* 985 drawopen(Chan *c, int omode) 986 { 987 Client *cl; 988 989 if(c->qid.type & QTDIR){ 990 c = devopen(c, omode, 0, 0, drawgen); 991 c->iounit = IOUNIT; 992 } 993 994 qlock(&sdraw.lk); 995 if(waserror()){ 996 qunlock(&sdraw.lk); 997 nexterror(); 998 } 999 1000 if(QID(c->qid) == Qnew){ 1001 cl = drawnewclient(); 1002 if(cl == 0) 1003 error(Enodev); 1004 c->qid.path = Qctl|((cl->slot+1)<<QSHIFT); 1005 } 1006 1007 switch(QID(c->qid)){ 1008 case Qnew: 1009 break; 1010 1011 case Qctl: 1012 cl = drawclient(c); 1013 if(cl->busy) 1014 error(Einuse); 1015 cl->busy = 1; 1016 flushrect = Rect(10000, 10000, -10000, -10000); 1017 drawinstall(cl, 0, screenimage, 0); 1018 incref(&cl->r); 1019 break; 1020 case Qcolormap: 1021 case Qdata: 1022 case Qrefresh: 1023 cl = drawclient(c); 1024 incref(&cl->r); 1025 break; 1026 } 1027 qunlock(&sdraw.lk); 1028 poperror(); 1029 c->mode = openmode(omode); 1030 c->flag |= COPEN; 1031 c->offset = 0; 1032 c->iounit = IOUNIT; 1033 return c; 1034 } 1035 1036 static void 1037 drawclose(Chan *c) 1038 { 1039 int i; 1040 DImage *d, **dp; 1041 Client *cl; 1042 Refresh *r; 1043 1044 if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ 1045 return; 1046 qlock(&sdraw.lk); 1047 if(waserror()){ 1048 qunlock(&sdraw.lk); 1049 nexterror(); 1050 } 1051 1052 cl = drawclient(c); 1053 if(QID(c->qid) == Qctl) 1054 cl->busy = 0; 1055 if((c->flag&COPEN) && (decref(&cl->r)==0)){ 1056 while((r = cl->refresh)){ /* assign = */ 1057 cl->refresh = r->next; 1058 free(r); 1059 } 1060 /* free names */ 1061 for(i=0; i<sdraw.nname; ) 1062 if(sdraw.name[i].client == cl) 1063 drawdelname(sdraw.name+i); 1064 else 1065 i++; 1066 while(cl->cscreen) 1067 drawuninstallscreen(cl, cl->cscreen); 1068 /* all screens are freed, so now we can free images */ 1069 dp = cl->dimage; 1070 for(i=0; i<NHASH; i++){ 1071 while((d = *dp) != nil){ 1072 *dp = d->next; 1073 drawfreedimage(d); 1074 } 1075 dp++; 1076 } 1077 sdraw.client[cl->slot] = 0; 1078 drawflush(); /* to erase visible, now dead windows */ 1079 free(cl); 1080 } 1081 qunlock(&sdraw.lk); 1082 poperror(); 1083 } 1084 1085 long 1086 drawread(Chan *c, void *a, long n, vlong off) 1087 { 1088 int index, m; 1089 ulong red, green, blue; 1090 Client *cl; 1091 uchar *p; 1092 Refresh *r; 1093 DImage *di; 1094 Memimage *i; 1095 ulong offset = off; 1096 char buf[16]; 1097 1098 if(c->qid.type & QTDIR) 1099 return devdirread(c, a, n, 0, 0, drawgen); 1100 cl = drawclient(c); 1101 qlock(&sdraw.lk); 1102 if(waserror()){ 1103 qunlock(&sdraw.lk); 1104 nexterror(); 1105 } 1106 switch(QID(c->qid)){ 1107 case Qctl: 1108 if(n < 12*12) 1109 error(Eshortread); 1110 if(cl->infoid < 0) 1111 error(Enodrawimage); 1112 if(cl->infoid == 0){ 1113 i = screenimage; 1114 if(i == nil) 1115 error(Enodrawimage); 1116 }else{ 1117 di = drawlookup(cl, cl->infoid, 1); 1118 if(di == nil) 1119 error(Enodrawimage); 1120 i = di->image; 1121 } 1122 n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", 1123 cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, 1124 i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, 1125 i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); 1126 cl->infoid = -1; 1127 break; 1128 1129 case Qcolormap: 1130 drawactive(1); /* to restore map from backup */ 1131 p = malloc(4*12*256+1); 1132 if(p == 0) 1133 error(Enomem); 1134 m = 0; 1135 for(index = 0; index < 256; index++){ 1136 getcolor(index, &red, &green, &blue); 1137 m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); 1138 } 1139 n = readstr(offset, a, n, (char*)p); 1140 free(p); 1141 break; 1142 1143 case Qdata: 1144 if(cl->readdata == nil) 1145 error("no draw data"); 1146 if(n < cl->nreaddata) 1147 error(Eshortread); 1148 n = cl->nreaddata; 1149 memmove(a, cl->readdata, cl->nreaddata); 1150 free(cl->readdata); 1151 cl->readdata = nil; 1152 break; 1153 1154 case Qrefresh: 1155 if(n < 5*4) 1156 error(Ebadarg); 1157 for(;;){ 1158 if(cl->refreshme || cl->refresh) 1159 break; 1160 qunlock(&sdraw.lk); 1161 if(waserror()){ 1162 qlock(&sdraw.lk); /* restore lock for waserror() above */ 1163 nexterror(); 1164 } 1165 sleep(&cl->refrend, drawrefactive, cl); 1166 poperror(); 1167 qlock(&sdraw.lk); 1168 } 1169 p = a; 1170 while(cl->refresh && n>=5*4){ 1171 r = cl->refresh; 1172 BPLONG(p+0*4, r->dimage->id); 1173 BPLONG(p+1*4, r->r.min.x); 1174 BPLONG(p+2*4, r->r.min.y); 1175 BPLONG(p+3*4, r->r.max.x); 1176 BPLONG(p+4*4, r->r.max.y); 1177 cl->refresh = r->next; 1178 free(r); 1179 p += 5*4; 1180 n -= 5*4; 1181 } 1182 cl->refreshme = 0; 1183 n = p-(uchar*)a; 1184 } 1185 qunlock(&sdraw.lk); 1186 poperror(); 1187 return n; 1188 } 1189 1190 void 1191 drawwakeall(void) 1192 { 1193 Client *cl; 1194 int i; 1195 1196 for(i=0; i<sdraw.nclient; i++){ 1197 cl = sdraw.client[i]; 1198 if(cl && (cl->refreshme || cl->refresh)) 1199 wakeup(&cl->refrend); 1200 } 1201 } 1202 1203 static long 1204 drawwrite(Chan *c, void *a, long n, vlong offset) 1205 { 1206 char buf[128], *fields[4], *q; 1207 Client *cl; 1208 int i, m, red, green, blue, x; 1209 1210 USED(offset); 1211 1212 if(c->qid.type & QTDIR) 1213 error(Eisdir); 1214 cl = drawclient(c); 1215 qlock(&sdraw.lk); 1216 if(waserror()){ 1217 drawwakeall(); 1218 qunlock(&sdraw.lk); 1219 nexterror(); 1220 } 1221 switch(QID(c->qid)){ 1222 case Qctl: 1223 if(n != 4) 1224 error("unknown draw control request"); 1225 cl->infoid = BGLONG((uchar*)a); 1226 break; 1227 1228 case Qcolormap: 1229 drawactive(1); /* to restore map from backup */ 1230 m = n; 1231 n = 0; 1232 while(m > 0){ 1233 x = m; 1234 if(x > sizeof(buf)-1) 1235 x = sizeof(buf)-1; 1236 q = memccpy(buf, a, '\n', x); 1237 if(q == 0) 1238 break; 1239 i = q-buf; 1240 n += i; 1241 a = (char*)a + i; 1242 m -= i; 1243 *q = 0; 1244 if(tokenize(buf, fields, nelem(fields)) != 4) 1245 error(Ebadarg); 1246 i = strtoul(fields[0], 0, 0); 1247 red = strtoul(fields[1], 0, 0); 1248 green = strtoul(fields[2], 0, 0); 1249 blue = strtoul(fields[3], &q, 0); 1250 if(fields[3] == q) 1251 error(Ebadarg); 1252 if(red>255 || green>255 || blue>255 || i<0 || i>255) 1253 error(Ebadarg); 1254 red |= red<<8; 1255 red |= red<<16; 1256 green |= green<<8; 1257 green |= green<<16; 1258 blue |= blue<<8; 1259 blue |= blue<<16; 1260 setcolor(i, red, green, blue); 1261 } 1262 break; 1263 1264 case Qdata: 1265 drawmesg(cl, a, n); 1266 drawwakeall(); 1267 break; 1268 1269 default: 1270 error(Ebadusefd); 1271 } 1272 qunlock(&sdraw.lk); 1273 poperror(); 1274 return n; 1275 } 1276 1277 uchar* 1278 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) 1279 { 1280 int b, x; 1281 1282 if(p >= maxp) 1283 error(Eshortdraw); 1284 b = *p++; 1285 x = b & 0x7F; 1286 if(b & 0x80){ 1287 if(p+1 >= maxp) 1288 error(Eshortdraw); 1289 x |= *p++ << 7; 1290 x |= *p++ << 15; 1291 if(x & (1<<22)) 1292 x |= ~0<<23; 1293 }else{ 1294 if(b & 0x40) 1295 x |= ~0<<7; 1296 x += oldx; 1297 } 1298 *newx = x; 1299 return p; 1300 } 1301 1302 static void 1303 printmesg(char *fmt, uchar *a, int plsprnt) 1304 { 1305 char buf[256]; 1306 char *p, *q; 1307 int s; 1308 1309 if(1|| plsprnt==0){ 1310 SET(s); 1311 SET(q); 1312 SET(p); 1313 USED(fmt); 1314 USED(a); 1315 p = buf; 1316 USED(p); 1317 USED(q); 1318 USED(s); 1319 return; 1320 } 1321 q = buf; 1322 *q++ = *a++; 1323 for(p=fmt; *p; p++){ 1324 switch(*p){ 1325 case 'l': 1326 q += sprint(q, " %ld", (long)BGLONG(a)); 1327 a += 4; 1328 break; 1329 case 'L': 1330 q += sprint(q, " %.8lux", (ulong)BGLONG(a)); 1331 a += 4; 1332 break; 1333 case 'R': 1334 q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); 1335 a += 16; 1336 break; 1337 case 'P': 1338 q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); 1339 a += 8; 1340 break; 1341 case 'b': 1342 q += sprint(q, " %d", *a++); 1343 break; 1344 case 's': 1345 q += sprint(q, " %d", BGSHORT(a)); 1346 a += 2; 1347 break; 1348 case 'S': 1349 q += sprint(q, " %.4ux", BGSHORT(a)); 1350 a += 2; 1351 break; 1352 } 1353 } 1354 *q++ = '\n'; 1355 *q = 0; 1356 iprint("%.*s", (int)(q-buf), buf); 1357 } 1358 1359 void 1360 drawmesg(Client *client, void *av, int n) 1361 { 1362 int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush; 1363 uchar *u, *a, refresh; 1364 char *fmt; 1365 ulong value, chan; 1366 Rectangle r, clipr; 1367 Point p, q, *pp, sp; 1368 Memimage *i, *dst, *src, *mask; 1369 Memimage *l, **lp; 1370 Memscreen *scrn; 1371 DImage *font, *ll, *di, *ddst, *dsrc; 1372 DName *dn; 1373 DScreen *dscrn; 1374 FChar *fc; 1375 Refx *refx; 1376 CScreen *cs; 1377 Refreshfn reffn; 1378 1379 a = av; 1380 m = 0; 1381 fmt = nil; 1382 if(waserror()){ 1383 if(fmt) printmesg(fmt, a, 1); 1384 /* iprint("error: %s\n", up->errstr); */ 1385 nexterror(); 1386 } 1387 while((n-=m) > 0){ 1388 USED(fmt); 1389 a += m; 1390 switch(*a){ 1391 default: 1392 error("bad draw command"); 1393 /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ 1394 case 'b': 1395 printmesg(fmt="LLbLbRRL", a, 0); 1396 m = 1+4+4+1+4+1+4*4+4*4+4; 1397 if(n < m) 1398 error(Eshortdraw); 1399 dstid = BGLONG(a+1); 1400 scrnid = BGSHORT(a+5); 1401 refresh = a[9]; 1402 chan = BGLONG(a+10); 1403 repl = a[14]; 1404 drawrectangle(&r, a+15); 1405 drawrectangle(&clipr, a+31); 1406 value = BGLONG(a+47); 1407 if(drawlookup(client, dstid, 0)) 1408 error(Eimageexists); 1409 if(scrnid){ 1410 dscrn = drawlookupscreen(client, scrnid, &cs); 1411 scrn = dscrn->screen; 1412 if(repl || chan!=scrn->image->chan) 1413 error("image parameters incompatible with screen"); 1414 reffn = 0; 1415 switch(refresh){ 1416 case Refbackup: 1417 break; 1418 case Refnone: 1419 reffn = memlnorefresh; 1420 break; 1421 case Refmesg: 1422 reffn = drawrefresh; 1423 break; 1424 default: 1425 error("unknown refresh method"); 1426 } 1427 l = memlalloc(scrn, r, reffn, 0, value); 1428 if(l == 0) 1429 error(Edrawmem); 1430 addflush(l->layer->screenr); 1431 l->clipr = clipr; 1432 rectclip(&l->clipr, r); 1433 if(drawinstall(client, dstid, l, dscrn) == 0){ 1434 memldelete(l); 1435 error(Edrawmem); 1436 } 1437 dscrn->ref++; 1438 if(reffn){ 1439 refx = nil; 1440 if(reffn == drawrefresh){ 1441 refx = malloc(sizeof(Refx)); 1442 if(refx == 0){ 1443 drawuninstall(client, dstid); 1444 error(Edrawmem); 1445 } 1446 refx->client = client; 1447 refx->dimage = drawlookup(client, dstid, 1); 1448 } 1449 memlsetrefresh(l, reffn, refx); 1450 } 1451 continue; 1452 } 1453 i = allocmemimage(r, chan); 1454 if(i == 0) 1455 error(Edrawmem); 1456 if(repl) 1457 i->flags |= Frepl; 1458 i->clipr = clipr; 1459 if(!repl) 1460 rectclip(&i->clipr, r); 1461 if(drawinstall(client, dstid, i, 0) == 0){ 1462 freememimage(i); 1463 error(Edrawmem); 1464 } 1465 memfillcolor(i, value); 1466 continue; 1467 1468 /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ 1469 case 'A': 1470 printmesg(fmt="LLLb", a, 1); 1471 m = 1+4+4+4+1; 1472 if(n < m) 1473 error(Eshortdraw); 1474 dstid = BGLONG(a+1); 1475 if(dstid == 0) 1476 error(Ebadarg); 1477 if(drawlookupdscreen(dstid)) 1478 error(Escreenexists); 1479 ddst = drawlookup(client, BGLONG(a+5), 1); 1480 dsrc = drawlookup(client, BGLONG(a+9), 1); 1481 if(ddst==0 || dsrc==0) 1482 error(Enodrawimage); 1483 if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) 1484 error(Edrawmem); 1485 continue; 1486 1487 /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ 1488 case 'c': 1489 printmesg(fmt="LbR", a, 0); 1490 m = 1+4+1+4*4; 1491 if(n < m) 1492 error(Eshortdraw); 1493 ddst = drawlookup(client, BGLONG(a+1), 1); 1494 if(ddst == nil) 1495 error(Enodrawimage); 1496 if(ddst->name) 1497 error("can't change repl/clipr of shared image"); 1498 dst = ddst->image; 1499 if(a[5]) 1500 dst->flags |= Frepl; 1501 drawrectangle(&dst->clipr, a+6); 1502 continue; 1503 1504 /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ 1505 case 'd': 1506 printmesg(fmt="LLLRPP", a, 0); 1507 m = 1+4+4+4+4*4+2*4+2*4; 1508 if(n < m) 1509 error(Eshortdraw); 1510 dst = drawimage(client, a+1); 1511 dstid = BGLONG(a+1); 1512 src = drawimage(client, a+5); 1513 mask = drawimage(client, a+9); 1514 drawrectangle(&r, a+13); 1515 drawpoint(&p, a+29); 1516 drawpoint(&q, a+37); 1517 op = drawclientop(client); 1518 memdraw(dst, r, src, p, mask, q, op); 1519 dstflush(dstid, dst, r); 1520 continue; 1521 1522 /* toggle debugging: 'D' val[1] */ 1523 case 'D': 1524 printmesg(fmt="b", a, 0); 1525 m = 1+1; 1526 if(n < m) 1527 error(Eshortdraw); 1528 drawdebug = a[1]; 1529 continue; 1530 1531 /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ 1532 case 'e': 1533 case 'E': 1534 printmesg(fmt="LLPlllPll", a, 0); 1535 m = 1+4+4+2*4+4+4+4+2*4+2*4; 1536 if(n < m) 1537 error(Eshortdraw); 1538 dst = drawimage(client, a+1); 1539 dstid = BGLONG(a+1); 1540 src = drawimage(client, a+5); 1541 drawpoint(&p, a+9); 1542 e0 = BGLONG(a+17); 1543 e1 = BGLONG(a+21); 1544 if(e0<0 || e1<0) 1545 error("invalid ellipse semidiameter"); 1546 j = BGLONG(a+25); 1547 if(j < 0) 1548 error("negative ellipse thickness"); 1549 drawpoint(&sp, a+29); 1550 c = j; 1551 if(*a == 'E') 1552 c = -1; 1553 ox = BGLONG(a+37); 1554 oy = BGLONG(a+41); 1555 op = drawclientop(client); 1556 /* high bit indicates arc angles are present */ 1557 if(ox & (1U<<31)){ 1558 if((ox & (1<<30)) == 0) 1559 ox &= ~(1U<<31); 1560 memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); 1561 }else 1562 memellipse(dst, p, e0, e1, c, src, sp, op); 1563 dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); 1564 continue; 1565 1566 /* free: 'f' id[4] */ 1567 case 'f': 1568 printmesg(fmt="L", a, 1); 1569 m = 1+4; 1570 if(n < m) 1571 error(Eshortdraw); 1572 ll = drawlookup(client, BGLONG(a+1), 0); 1573 if(ll && ll->dscreen && ll->dscreen->owner != client) 1574 ll->dscreen->owner->refreshme = 1; 1575 drawuninstall(client, BGLONG(a+1)); 1576 continue; 1577 1578 /* free screen: 'F' id[4] */ 1579 case 'F': 1580 printmesg(fmt="L", a, 1); 1581 m = 1+4; 1582 if(n < m) 1583 error(Eshortdraw); 1584 drawlookupscreen(client, BGLONG(a+1), &cs); 1585 drawuninstallscreen(client, cs); 1586 continue; 1587 1588 /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ 1589 case 'i': 1590 printmesg(fmt="Llb", a, 1); 1591 m = 1+4+4+1; 1592 if(n < m) 1593 error(Eshortdraw); 1594 dstid = BGLONG(a+1); 1595 if(dstid == 0) 1596 error("can't use display as font"); 1597 font = drawlookup(client, dstid, 1); 1598 if(font == 0) 1599 error(Enodrawimage); 1600 if(font->image->layer) 1601 error("can't use window as font"); 1602 ni = BGLONG(a+5); 1603 if(ni<=0 || ni>4096) 1604 error("bad font size (4096 chars max)"); 1605 free(font->fchar); /* should we complain if non-zero? */ 1606 font->fchar = malloc(ni*sizeof(FChar)); 1607 if(font->fchar == 0) 1608 error("no memory for font"); 1609 memset(font->fchar, 0, ni*sizeof(FChar)); 1610 font->nfchar = ni; 1611 font->ascent = a[9]; 1612 continue; 1613 1614 /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ 1615 case 'l': 1616 printmesg(fmt="LLSRPbb", a, 0); 1617 m = 1+4+4+2+4*4+2*4+1+1; 1618 if(n < m) 1619 error(Eshortdraw); 1620 font = drawlookup(client, BGLONG(a+1), 1); 1621 if(font == 0) 1622 error(Enodrawimage); 1623 if(font->nfchar == 0) 1624 error(Enotfont); 1625 src = drawimage(client, a+5); 1626 ci = BGSHORT(a+9); 1627 if(ci >= font->nfchar) 1628 error(Eindex); 1629 drawrectangle(&r, a+11); 1630 drawpoint(&p, a+27); 1631 memdraw(font->image, r, src, p, memopaque, p, S); 1632 fc = &font->fchar[ci]; 1633 fc->minx = r.min.x; 1634 fc->maxx = r.max.x; 1635 fc->miny = r.min.y; 1636 fc->maxy = r.max.y; 1637 fc->left = a[35]; 1638 fc->width = a[36]; 1639 continue; 1640 1641 /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ 1642 case 'L': 1643 printmesg(fmt="LPPlllLP", a, 0); 1644 m = 1+4+2*4+2*4+4+4+4+4+2*4; 1645 if(n < m) 1646 error(Eshortdraw); 1647 dst = drawimage(client, a+1); 1648 dstid = BGLONG(a+1); 1649 drawpoint(&p, a+5); 1650 drawpoint(&q, a+13); 1651 e0 = BGLONG(a+21); 1652 e1 = BGLONG(a+25); 1653 j = BGLONG(a+29); 1654 if(j < 0) 1655 error("negative line width"); 1656 src = drawimage(client, a+33); 1657 drawpoint(&sp, a+37); 1658 op = drawclientop(client); 1659 memline(dst, p, q, e0, e1, j, src, sp, op); 1660 /* avoid memlinebbox if possible */ 1661 if(dstid==0 || dst->layer!=nil){ 1662 /* BUG: this is terribly inefficient: update maximal containing rect*/ 1663 r = memlinebbox(p, q, e0, e1, j); 1664 dstflush(dstid, dst, insetrect(r, -(1+1+j))); 1665 } 1666 continue; 1667 1668 /* create image mask: 'm' newid[4] id[4] */ 1669 /* 1670 * 1671 case 'm': 1672 printmesg("LL", a, 0); 1673 m = 4+4; 1674 if(n < m) 1675 error(Eshortdraw); 1676 break; 1677 * 1678 */ 1679 1680 /* attach to a named image: 'n' dstid[4] j[1] name[j] */ 1681 case 'n': 1682 printmesg(fmt="Lz", a, 0); 1683 m = 1+4+1; 1684 if(n < m) 1685 error(Eshortdraw); 1686 j = a[5]; 1687 if(j == 0) /* give me a non-empty name please */ 1688 error(Eshortdraw); 1689 m += j; 1690 if(n < m) 1691 error(Eshortdraw); 1692 dstid = BGLONG(a+1); 1693 if(drawlookup(client, dstid, 0)) 1694 error(Eimageexists); 1695 dn = drawlookupname(j, (char*)a+6); 1696 if(dn == nil) 1697 error(Enoname); 1698 if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) 1699 error(Edrawmem); 1700 di = drawlookup(client, dstid, 0); 1701 if(di == 0) 1702 error("draw: can't happen"); 1703 di->vers = dn->vers; 1704 di->name = smalloc(j+1); 1705 di->fromname = dn->dimage; 1706 di->fromname->ref++; 1707 memmove(di->name, a+6, j); 1708 di->name[j] = 0; 1709 client->infoid = dstid; 1710 continue; 1711 1712 /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ 1713 case 'N': 1714 printmesg(fmt="Lbz", a, 0); 1715 m = 1+4+1+1; 1716 if(n < m) 1717 error(Eshortdraw); 1718 c = a[5]; 1719 j = a[6]; 1720 if(j == 0) /* give me a non-empty name please */ 1721 error(Eshortdraw); 1722 m += j; 1723 if(n < m) 1724 error(Eshortdraw); 1725 di = drawlookup(client, BGLONG(a+1), 0); 1726 if(di == 0) 1727 error(Enodrawimage); 1728 if(di->name) 1729 error(Enamed); 1730 if(c) 1731 drawaddname(client, di, j, (char*)a+7); 1732 else{ 1733 dn = drawlookupname(j, (char*)a+7); 1734 if(dn == nil) 1735 error(Enoname); 1736 if(dn->dimage != di) 1737 error(Ewrongname); 1738 drawdelname(dn); 1739 } 1740 continue; 1741 1742 /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ 1743 case 'o': 1744 printmesg(fmt="LPP", a, 0); 1745 m = 1+4+2*4+2*4; 1746 if(n < m) 1747 error(Eshortdraw); 1748 dst = drawimage(client, a+1); 1749 if(dst->layer){ 1750 drawpoint(&p, a+5); 1751 drawpoint(&q, a+13); 1752 r = dst->layer->screenr; 1753 ni = memlorigin(dst, p, q); 1754 if(ni < 0) 1755 error("image origin failed"); 1756 if(ni > 0){ 1757 addflush(r); 1758 addflush(dst->layer->screenr); 1759 ll = drawlookup(client, BGLONG(a+1), 1); 1760 drawrefreshscreen(ll, client); 1761 } 1762 } 1763 continue; 1764 1765 /* set compositing operator for next draw operation: 'O' op */ 1766 case 'O': 1767 printmesg(fmt="b", a, 0); 1768 m = 1+1; 1769 if(n < m) 1770 error(Eshortdraw); 1771 client->op = a[1]; 1772 continue; 1773 1774 /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1775 /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ 1776 case 'p': 1777 case 'P': 1778 printmesg(fmt="LslllLPP", a, 0); 1779 m = 1+4+2+4+4+4+4+2*4; 1780 if(n < m) 1781 error(Eshortdraw); 1782 dstid = BGLONG(a+1); 1783 dst = drawimage(client, a+1); 1784 ni = BGSHORT(a+5); 1785 if(ni < 0) 1786 error("negative count in polygon"); 1787 e0 = BGLONG(a+7); 1788 e1 = BGLONG(a+11); 1789 j = 0; 1790 if(*a == 'p'){ 1791 j = BGLONG(a+15); 1792 if(j < 0) 1793 error("negative polygon line width"); 1794 } 1795 src = drawimage(client, a+19); 1796 drawpoint(&sp, a+23); 1797 drawpoint(&p, a+31); 1798 ni++; 1799 pp = malloc(ni*sizeof(Point)); 1800 if(pp == nil) 1801 error(Enomem); 1802 doflush = 0; 1803 if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) 1804 doflush = 1; /* simplify test in loop */ 1805 ox = oy = 0; 1806 esize = 0; 1807 u = a+m; 1808 for(y=0; y<ni; y++){ 1809 q = p; 1810 oesize = esize; 1811 u = drawcoord(u, a+n, ox, &p.x); 1812 u = drawcoord(u, a+n, oy, &p.y); 1813 ox = p.x; 1814 oy = p.y; 1815 if(doflush){ 1816 esize = j; 1817 if(*a == 'p'){ 1818 if(y == 0){ 1819 c = memlineendsize(e0); 1820 if(c > esize) 1821 esize = c; 1822 } 1823 if(y == ni-1){ 1824 c = memlineendsize(e1); 1825 if(c > esize) 1826 esize = c; 1827 } 1828 } 1829 if(*a=='P' && e0!=1 && e0 !=~0) 1830 r = dst->clipr; 1831 else if(y > 0){ 1832 r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); 1833 combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1834 } 1835 if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ 1836 dstflush(dstid, dst, r); 1837 } 1838 pp[y] = p; 1839 } 1840 if(y == 1) 1841 dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); 1842 op = drawclientop(client); 1843 if(*a == 'p') 1844 mempoly(dst, pp, ni, e0, e1, j, src, sp, op); 1845 else 1846 memfillpoly(dst, pp, ni, e0, src, sp, op); 1847 free(pp); 1848 m = u-a; 1849 continue; 1850 1851 /* read: 'r' id[4] R[4*4] */ 1852 case 'r': 1853 printmesg(fmt="LR", a, 0); 1854 m = 1+4+4*4; 1855 if(n < m) 1856 error(Eshortdraw); 1857 i = drawimage(client, a+1); 1858 drawrectangle(&r, a+5); 1859 if(!rectinrect(r, i->r)) 1860 error(Ereadoutside); 1861 c = bytesperline(r, i->depth); 1862 c *= Dy(r); 1863 free(client->readdata); 1864 client->readdata = mallocz(c, 0); 1865 if(client->readdata == nil) 1866 error("readimage malloc failed"); 1867 client->nreaddata = memunload(i, r, client->readdata, c); 1868 if(client->nreaddata < 0){ 1869 free(client->readdata); 1870 client->readdata = nil; 1871 error("bad readimage call"); 1872 } 1873 continue; 1874 1875 /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ 1876 /* 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]) */ 1877 case 's': 1878 case 'x': 1879 printmesg(fmt="LLLPRPs", a, 0); 1880 m = 1+4+4+4+2*4+4*4+2*4+2; 1881 if(*a == 'x') 1882 m += 4+2*4; 1883 if(n < m) 1884 error(Eshortdraw); 1885 1886 dst = drawimage(client, a+1); 1887 dstid = BGLONG(a+1); 1888 src = drawimage(client, a+5); 1889 font = drawlookup(client, BGLONG(a+9), 1); 1890 if(font == 0) 1891 error(Enodrawimage); 1892 if(font->nfchar == 0) 1893 error(Enotfont); 1894 drawpoint(&p, a+13); 1895 drawrectangle(&r, a+21); 1896 drawpoint(&sp, a+37); 1897 ni = BGSHORT(a+45); 1898 u = a+m; 1899 m += ni*2; 1900 if(n < m) 1901 error(Eshortdraw); 1902 clipr = dst->clipr; 1903 dst->clipr = r; 1904 op = drawclientop(client); 1905 l = dst; 1906 if(*a == 'x'){ 1907 /* paint background */ 1908 l = drawimage(client, a+47); 1909 drawpoint(&q, a+51); 1910 r.min.x = p.x; 1911 r.min.y = p.y-font->ascent; 1912 r.max.x = p.x; 1913 r.max.y = r.min.y+Dy(font->image->r); 1914 j = ni; 1915 while(--j >= 0){ 1916 ci = BGSHORT(u); 1917 if(ci<0 || ci>=font->nfchar){ 1918 dst->clipr = clipr; 1919 error(Eindex); 1920 } 1921 r.max.x += font->fchar[ci].width; 1922 u += 2; 1923 } 1924 memdraw(dst, r, l, q, memopaque, ZP, op); 1925 u -= 2*ni; 1926 } 1927 q = p; 1928 while(--ni >= 0){ 1929 ci = BGSHORT(u); 1930 if(ci<0 || ci>=font->nfchar){ 1931 dst->clipr = clipr; 1932 error(Eindex); 1933 } 1934 q = drawchar(dst, l, q, src, &sp, font, ci, op); 1935 u += 2; 1936 } 1937 dst->clipr = clipr; 1938 p.y -= font->ascent; 1939 dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); 1940 continue; 1941 1942 /* use public screen: 'S' id[4] chan[4] */ 1943 case 'S': 1944 printmesg(fmt="Ll", a, 0); 1945 m = 1+4+4; 1946 if(n < m) 1947 error(Eshortdraw); 1948 dstid = BGLONG(a+1); 1949 if(dstid == 0) 1950 error(Ebadarg); 1951 dscrn = drawlookupdscreen(dstid); 1952 if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) 1953 error(Enodrawscreen); 1954 if(dscrn->screen->image->chan != BGLONG(a+5)) 1955 error("inconsistent chan"); 1956 if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) 1957 error(Edrawmem); 1958 continue; 1959 1960 /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ 1961 case 't': 1962 printmesg(fmt="bsL", a, 0); 1963 m = 1+1+2; 1964 if(n < m) 1965 error(Eshortdraw); 1966 nw = BGSHORT(a+2); 1967 if(nw < 0) 1968 error(Ebadarg); 1969 if(nw == 0) 1970 continue; 1971 m += nw*4; 1972 if(n < m) 1973 error(Eshortdraw); 1974 lp = malloc(nw*sizeof(Memimage*)); 1975 if(lp == 0) 1976 error(Enomem); 1977 if(waserror()){ 1978 free(lp); 1979 nexterror(); 1980 } 1981 for(j=0; j<nw; j++) 1982 lp[j] = drawimage(client, a+1+1+2+j*4); 1983 if(lp[0]->layer == 0) 1984 error("images are not windows"); 1985 for(j=1; j<nw; j++) 1986 if(lp[j]->layer->screen != lp[0]->layer->screen) 1987 error("images not on same screen"); 1988 if(a[1]) 1989 memltofrontn(lp, nw); 1990 else 1991 memltorearn(lp, nw); 1992 if(lp[0]->layer->screen->image->data == screenimage->data) 1993 for(j=0; j<nw; j++) 1994 addflush(lp[j]->layer->screenr); 1995 ll = drawlookup(client, BGLONG(a+1+1+2), 1); 1996 drawrefreshscreen(ll, client); 1997 poperror(); 1998 free(lp); 1999 continue; 2000 2001 /* visible: 'v' */ 2002 case 'v': 2003 printmesg(fmt="", a, 0); 2004 m = 1; 2005 drawflush(); 2006 continue; 2007 2008 /* write: 'y' id[4] R[4*4] data[x*1] */ 2009 /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ 2010 case 'y': 2011 case 'Y': 2012 printmesg(fmt="LR", a, 0); 2013 // iprint("load %c\n", *a); 2014 m = 1+4+4*4; 2015 if(n < m) 2016 error(Eshortdraw); 2017 dstid = BGLONG(a+1); 2018 dst = drawimage(client, a+1); 2019 drawrectangle(&r, a+5); 2020 if(!rectinrect(r, dst->r)) 2021 error(Ewriteoutside); 2022 y = memload(dst, r, a+m, n-m, *a=='Y'); 2023 if(y < 0) 2024 error("bad writeimage call"); 2025 dstflush(dstid, dst, r); 2026 m += y; 2027 continue; 2028 } 2029 } 2030 poperror(); 2031 } 2032 2033 Dev drawdevtab = { 2034 'i', 2035 "draw", 2036 2037 devreset, 2038 devinit, 2039 devshutdown, 2040 drawattach, 2041 drawwalk, 2042 drawstat, 2043 drawopen, 2044 devcreate, 2045 drawclose, 2046 drawread, 2047 devbread, 2048 drawwrite, 2049 devbwrite, 2050 devremove, 2051 devwstat, 2052 }; 2053 2054 /* 2055 * On 8 bit displays, load the default color map 2056 */ 2057 void 2058 drawcmap(void) 2059 { 2060 int r, g, b, cr, cg, cb, v; 2061 int num, den; 2062 int i, j; 2063 2064 drawactive(1); /* to restore map from backup */ 2065 for(r=0,i=0; r!=4; r++) 2066 for(v=0; v!=4; v++,i+=16){ 2067 for(g=0,j=v-r; g!=4; g++) 2068 for(b=0;b!=4;b++,j++){ 2069 den = r; 2070 if(g > den) 2071 den = g; 2072 if(b > den) 2073 den = b; 2074 if(den == 0) /* divide check -- pick grey shades */ 2075 cr = cg = cb = v*17; 2076 else{ 2077 num = 17*(4*den+v); 2078 cr = r*num/den; 2079 cg = g*num/den; 2080 cb = b*num/den; 2081 } 2082 setcolor(i+(j&15), 2083 cr*0x01010101, cg*0x01010101, cb*0x01010101); 2084 } 2085 } 2086 } 2087 2088 void 2089 drawblankscreen(int blank) 2090 { 2091 int i, nc; 2092 ulong *p; 2093 2094 if(blank == sdraw.blanked) 2095 return; 2096 if(!canqlock(&sdraw.lk)) 2097 return; 2098 if(!initscreenimage()){ 2099 qunlock(&sdraw.lk); 2100 return; 2101 } 2102 p = sdraw.savemap; 2103 nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth; 2104 2105 /* 2106 * blankscreen uses the hardware to blank the screen 2107 * when possible. to help in cases when it is not possible, 2108 * we set the color map to be all black. 2109 */ 2110 if(blank == 0){ /* turn screen on */ 2111 for(i=0; i<nc; i++, p+=3) 2112 setcolor(i, p[0], p[1], p[2]); 2113 // blankscreen(0); 2114 }else{ /* turn screen off */ 2115 // blankscreen(1); 2116 for(i=0; i<nc; i++, p+=3){ 2117 getcolor(i, &p[0], &p[1], &p[2]); 2118 setcolor(i, 0, 0, 0); 2119 } 2120 } 2121 sdraw.blanked = blank; 2122 qunlock(&sdraw.lk); 2123 } 2124 2125 /* 2126 * record activity on screen, changing blanking as appropriate 2127 */ 2128 void 2129 drawactive(int active) 2130 { 2131 /* 2132 if(active){ 2133 drawblankscreen(0); 2134 sdraw.blanktime = MACHP(0)->ticks; 2135 }else{ 2136 if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime) 2137 drawblankscreen(1); 2138 } 2139 */ 2140 } 2141 2142 int 2143 drawidletime(void) 2144 { 2145 return 0; 2146 /* return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; */ 2147 } 2148 2149