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